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"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConsistentHash 一致性哈希负载均衡器。
|
// ConsistentHash 一致性哈希负载均衡器。
|
||||||
@ -31,6 +32,7 @@ type ConsistentHash struct {
|
|||||||
sortedHashes []uint64
|
sortedHashes []uint64
|
||||||
virtualNodes int
|
virtualNodes int
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
rebuilt atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConsistentHash 创建一致性哈希负载均衡器。
|
// NewConsistentHash 创建一致性哈希负载均衡器。
|
||||||
@ -66,33 +68,22 @@ func (c *ConsistentHash) Select(targets []*Target) *Target {
|
|||||||
// 返回值:
|
// 返回值:
|
||||||
// - *Target: 选中的目标,如果没有健康目标则返回 nil
|
// - *Target: 选中的目标,如果没有健康目标则返回 nil
|
||||||
func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
||||||
|
c.ensureRebuilt(targets)
|
||||||
|
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
// 如果环为空,重建哈希环
|
|
||||||
if len(c.circle) == 0 {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
c.rebuildCircle(targets)
|
|
||||||
c.mu.RLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.sortedHashes) == 0 {
|
if len(c.sortedHashes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算键的哈希值
|
hash := fnvHash64a(key)
|
||||||
hash := c.hashKeyString(key)
|
|
||||||
|
|
||||||
// 二分查找最近的节点
|
|
||||||
idx := sort.Search(len(c.sortedHashes), func(i int) bool {
|
idx := sort.Search(len(c.sortedHashes), func(i int) bool {
|
||||||
return c.sortedHashes[i] >= hash
|
return c.sortedHashes[i] >= hash
|
||||||
})
|
})
|
||||||
|
|
||||||
// 环形回绕
|
|
||||||
if idx >= len(c.sortedHashes) {
|
if idx >= len(c.sortedHashes) {
|
||||||
idx = 0
|
idx = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.circle[c.sortedHashes[idx]]
|
return c.circle[c.sortedHashes[idx]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +94,14 @@ func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
|||||||
// 参数:
|
// 参数:
|
||||||
// - targets: 新的目标列表
|
// - targets: 新的目标列表
|
||||||
func (c *ConsistentHash) Rebuild(targets []*Target) {
|
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)
|
c.rebuildCircle(targets)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +138,7 @@ func (c *ConsistentHash) rebuildCircle(targets []*Target) {
|
|||||||
|
|
||||||
// 排序哈希值
|
// 排序哈希值
|
||||||
slices.Sort(c.sortedHashes)
|
slices.Sort(c.sortedHashes)
|
||||||
|
c.rebuilt.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashKeyString 计算字符串的哈希值(使用 FNV-64a)。
|
// hashKeyString 计算字符串的哈希值(使用 FNV-64a)。
|
||||||
@ -234,14 +234,9 @@ func (c *ConsistentHash) SelectExcluding(targets []*Target, excluded []*Target)
|
|||||||
// 若不一致(如多上游组场景),targetSet 校验将拒绝所有候选,返回 nil。
|
// 若不一致(如多上游组场景),targetSet 校验将拒绝所有候选,返回 nil。
|
||||||
// 调用方应在 targets 列表变化时调用 Rebuild() 更新主哈希环。
|
// 调用方应在 targets 列表变化时调用 Rebuild() 更新主哈希环。
|
||||||
func (c *ConsistentHash) SelectExcludingByKey(targets []*Target, excluded []*Target, key string) *Target {
|
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()
|
fc := acquireFilterContext()
|
||||||
defer releaseFilterContext(fc)
|
defer releaseFilterContext(fc)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user