xfy c6e7091089 perf(loadbalance): eliminate per-request allocations in filterHealthy with sync.Pool
filterHealthy() allocated 2 slices (available + backups) per call.
filterHealthyAndExclude() allocated 3 (adds excludeSet map).
IPHash allocated fnv.New64a() object per call.
All triggered on every request's LB selection.

Changes:
- Add filterContext struct holding reusable buffers, managed by sync.Pool
- Replace filterHealthy → filterInto (writes into pooled buffers)
- Replace filterHealthyAndExclude → filterIntoExcluding (pooled buffers)
- Add inline fnvHash64a() to avoid fnv.New64a() heap allocation
- Update all 6 balancer algorithms (RoundRobin, WeightedRoundRobin,
  LeastConnections, IPHash, Random, ConsistentHash) to use pooled
  filterContext via acquire/release pattern
- ConsistentHash.SelectExcludingByKey also uses pool for targetSet
- Remove buildExcludeSet (merged into filterIntoExcluding)

Result: allocs/op reduced from 2-3 to 0-1 on all LB Select paths.
2026-06-04 00:19:04 +08:00

81 lines
2.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package loadbalance 提供多种负载均衡算法实现,支持随机、轮询、加权、一致性哈希等策略。
//
// 包含随机负载均衡算法相关的逻辑。
//
// 作者xfy
package loadbalance
import (
"math/rand/v2"
"sync/atomic"
)
// Random 实现随机负载均衡Power of Two Choices 算法)。
//
// 随机选择两个候选目标,从中选择连接数较少的那个。
// 相比纯随机Power of Two Choices 能更好地均衡负载,
// 同时保持 O(1) 的选择复杂度。
//
// 当只有一个候选目标时直接返回;当 MaxConns 限制生效时
// 自动跳过已满的目标。
type Random struct{}
// NewRandom 创建一个新的随机负载均衡器。
func NewRandom() *Random {
return &Random{}
}
// Select 使用 Power of Two Choices 算法选择目标。
// 随机选择两个候选,返回连接数较少的那个。
// 只考虑可用目标。如果没有可用目标则返回 nil。
func (r *Random) Select(targets []*Target) *Target {
fc := acquireFilterContext()
defer releaseFilterContext(fc)
available := filterInto(fc, targets)
if len(available) == 0 {
return nil
}
if len(available) == 1 {
return available[0]
}
i := rand.IntN(len(available))
j := rand.IntN(len(available) - 1)
if j >= i {
j++
}
a, b := available[i], available[j]
if atomic.LoadInt64(&a.Connections) <= atomic.LoadInt64(&b.Connections) {
return a
}
return b
}
// SelectExcluding 使用 Power of Two Choices 算法选择目标,排除指定的目标列表。
func (r *Random) SelectExcluding(targets []*Target, excluded []*Target) *Target {
fc := acquireFilterContext()
defer releaseFilterContext(fc)
available := filterIntoExcluding(fc, targets, excluded)
if len(available) == 0 {
return nil
}
if len(available) == 1 {
return available[0]
}
i := rand.IntN(len(available))
j := rand.IntN(len(available) - 1)
if j >= i {
j++
}
a, b := available[i], available[j]
if atomic.LoadInt64(&a.Connections) <= atomic.LoadInt64(&b.Connections) {
return a
}
return b
}