perf(loadbalance): replace failMu mutex with atomic operations in Target
Remove sync.Mutex from Target's failure tracking hot path: - failCount int64 → atomic.Int64 - failedUntil int64 → atomic.Int64 - IsAvailable(): Load() instead of Lock/Unlock, eliminating mutex from every per-target check in every LB Select() call - RecordFailure(): Add(1) + Store() instead of Lock/Unlock - RecordSuccess(): Store(0) instead of Lock/Unlock This removes the only mutex acquisition in the load balancing selection path. Every Select() call iterates targets and calls IsAvailable() on each — previously acquiring failMu per target when MaxFails > 0. Now fully lock-free.
This commit is contained in:
parent
d8a0818ab2
commit
d03c180f62
@ -63,10 +63,8 @@ type Target struct {
|
|||||||
// ProxyURI 代理传递的 URI 路径
|
// ProxyURI 代理传递的 URI 路径
|
||||||
ProxyURI string
|
ProxyURI string
|
||||||
|
|
||||||
// failMu 保护 failCount 和 failedUntil 的协调更新
|
failCount atomic.Int64
|
||||||
failMu sync.Mutex
|
failedUntil atomic.Int64
|
||||||
failCount int64
|
|
||||||
failedUntil int64
|
|
||||||
|
|
||||||
// 慢启动相关字段
|
// 慢启动相关字段
|
||||||
// EffectiveWeight 当前有效权重(慢启动期间动态变化)
|
// EffectiveWeight 当前有效权重(慢启动期间动态变化)
|
||||||
@ -298,17 +296,17 @@ func (t *Target) IsAvailable() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if t.MaxFails > 0 {
|
if t.MaxFails > 0 {
|
||||||
t.failMu.Lock()
|
failCount := t.failCount.Load()
|
||||||
if t.failCount >= t.MaxFails && time.Now().UnixNano() < t.failedUntil {
|
if failCount >= t.MaxFails {
|
||||||
t.failMu.Unlock()
|
failedUntil := t.failedUntil.Load()
|
||||||
return false
|
if time.Now().UnixNano() < failedUntil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if failedUntil > 0 {
|
||||||
|
t.failCount.Store(0)
|
||||||
|
t.failedUntil.Store(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 冷却已过期,重置软状态
|
|
||||||
if t.failCount >= t.MaxFails && t.failedUntil > 0 {
|
|
||||||
t.failCount = 0
|
|
||||||
t.failedUntil = 0
|
|
||||||
}
|
|
||||||
t.failMu.Unlock()
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -320,17 +318,14 @@ func (t *Target) RecordFailure() int64 {
|
|||||||
if t.MaxFails <= 0 {
|
if t.MaxFails <= 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
t.failMu.Lock()
|
count := t.failCount.Add(1)
|
||||||
t.failCount++
|
|
||||||
count := t.failCount
|
|
||||||
if count >= t.MaxFails {
|
if count >= t.MaxFails {
|
||||||
timeout := t.FailTimeout
|
timeout := t.FailTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 10 * time.Second
|
timeout = 10 * time.Second
|
||||||
}
|
}
|
||||||
t.failedUntil = time.Now().Add(timeout).UnixNano()
|
t.failedUntil.Store(time.Now().Add(timeout).UnixNano())
|
||||||
}
|
}
|
||||||
t.failMu.Unlock()
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,10 +335,8 @@ func (t *Target) RecordSuccess() {
|
|||||||
if t.MaxFails <= 0 {
|
if t.MaxFails <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.failMu.Lock()
|
t.failCount.Store(0)
|
||||||
t.failCount = 0
|
t.failedUntil.Store(0)
|
||||||
t.failedUntil = 0
|
|
||||||
t.failMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBackup 返回目标是否为备份服务器。
|
// IsBackup 返回目标是否为备份服务器。
|
||||||
|
|||||||
@ -1819,11 +1819,9 @@ func TestTargetRecordSuccess(t *testing.T) {
|
|||||||
target.RecordFailure()
|
target.RecordFailure()
|
||||||
target.RecordFailure()
|
target.RecordFailure()
|
||||||
target.RecordSuccess()
|
target.RecordSuccess()
|
||||||
target.failMu.Lock()
|
if target.failCount.Load() != 0 {
|
||||||
if target.failCount != 0 {
|
|
||||||
t.Error("fail count should be reset after success")
|
t.Error("fail count should be reset after success")
|
||||||
}
|
}
|
||||||
target.failMu.Unlock()
|
|
||||||
if !target.IsAvailable() {
|
if !target.IsAvailable() {
|
||||||
t.Error("target should be available after success resets cooldown")
|
t.Error("target should be available after success resets cooldown")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user