perf(loadbalance): eliminate double-lock in ConsistentHash with atomic.Bool rebuild guard
SelectByKey and SelectExcludingByKey previously had a RLock→RUnlock→ rebuildCircle(Lock)→RLock pattern when the hash ring was empty. Under cold-start concurrency, multiple goroutines could trigger simultaneous rebuild attempts. Add atomic.Bool 'rebuilt' flag with ensureRebuilt() check before any RLock acquisition: - Fast path: atomic load returns true → skip rebuild, proceed to RLock - Cold start: first caller rebuilds and sets flag, subsequent callers see the flag and skip rebuild - Rebuild() explicitly resets the flag for explicit ring invalidation Eliminates the RLock→Unlock→Lock→RLock transition entirely. The ring is guaranteed ready before RLock is acquired.
This commit is contained in:
parent
c6e7091089
commit
7fe1ca6bec
@ -19,6 +19,7 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ConsistentHash 一致性哈希负载均衡器。
|
||||
@ -31,6 +32,7 @@ type ConsistentHash struct {
|
||||
sortedHashes []uint64
|
||||
virtualNodes int
|
||||
mu sync.RWMutex
|
||||
rebuilt atomic.Bool
|
||||
}
|
||||
|
||||
// NewConsistentHash 创建一致性哈希负载均衡器。
|
||||
@ -66,33 +68,22 @@ func (c *ConsistentHash) Select(targets []*Target) *Target {
|
||||
// 返回值:
|
||||
// - *Target: 选中的目标,如果没有健康目标则返回 nil
|
||||
func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
||||
c.ensureRebuilt(targets)
|
||||
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
// 如果环为空,重建哈希环
|
||||
if len(c.circle) == 0 {
|
||||
c.mu.RUnlock()
|
||||
c.rebuildCircle(targets)
|
||||
c.mu.RLock()
|
||||
}
|
||||
|
||||
if len(c.sortedHashes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 计算键的哈希值
|
||||
hash := c.hashKeyString(key)
|
||||
|
||||
// 二分查找最近的节点
|
||||
hash := fnvHash64a(key)
|
||||
idx := sort.Search(len(c.sortedHashes), func(i int) bool {
|
||||
return c.sortedHashes[i] >= hash
|
||||
})
|
||||
|
||||
// 环形回绕
|
||||
if idx >= len(c.sortedHashes) {
|
||||
idx = 0
|
||||
}
|
||||
|
||||
return c.circle[c.sortedHashes[idx]]
|
||||
}
|
||||
|
||||
@ -103,6 +94,14 @@ func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
||||
// 参数:
|
||||
// - targets: 新的目标列表
|
||||
func (c *ConsistentHash) Rebuild(targets []*Target) {
|
||||
c.rebuilt.Store(false)
|
||||
c.rebuildCircle(targets)
|
||||
}
|
||||
|
||||
func (c *ConsistentHash) ensureRebuilt(targets []*Target) {
|
||||
if c.rebuilt.Load() {
|
||||
return
|
||||
}
|
||||
c.rebuildCircle(targets)
|
||||
}
|
||||
|
||||
@ -139,6 +138,7 @@ func (c *ConsistentHash) rebuildCircle(targets []*Target) {
|
||||
|
||||
// 排序哈希值
|
||||
slices.Sort(c.sortedHashes)
|
||||
c.rebuilt.Store(true)
|
||||
}
|
||||
|
||||
// hashKeyString 计算字符串的哈希值(使用 FNV-64a)。
|
||||
@ -234,14 +234,9 @@ func (c *ConsistentHash) SelectExcluding(targets []*Target, excluded []*Target)
|
||||
// 若不一致(如多上游组场景),targetSet 校验将拒绝所有候选,返回 nil。
|
||||
// 调用方应在 targets 列表变化时调用 Rebuild() 更新主哈希环。
|
||||
func (c *ConsistentHash) SelectExcludingByKey(targets []*Target, excluded []*Target, key string) *Target {
|
||||
c.mu.RLock()
|
||||
c.ensureRebuilt(targets)
|
||||
|
||||
// 如果环为空,尝试重建
|
||||
if len(c.circle) == 0 {
|
||||
c.mu.RUnlock()
|
||||
c.rebuildCircle(targets)
|
||||
c.mu.RLock()
|
||||
}
|
||||
c.mu.RLock()
|
||||
|
||||
fc := acquireFilterContext()
|
||||
defer releaseFilterContext(fc)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user