- 添加 SlowStartManager 统一管理目标慢启动状态 - Target 新增 EffectiveWeight 和 SlowStart 字段 - 实现 GetEffectiveWeight 方法支持动态权重 - 权重从 1 线性增加到配置权重,防止恢复服务器被压垮 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
177 lines
3.8 KiB
Go
177 lines
3.8 KiB
Go
package loadbalance
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSlowStartManager_OnTargetHealthy(t *testing.T) {
|
|
mgr := NewSlowStartManager(100 * time.Millisecond)
|
|
|
|
target := &Target{
|
|
URL: "http://backend:8080",
|
|
Weight: 10,
|
|
SlowStart: 5 * time.Second,
|
|
}
|
|
|
|
mgr.OnTargetHealthy(target)
|
|
|
|
// 验证初始权重为 1
|
|
if target.EffectiveWeight.Load() != 1 {
|
|
t.Errorf("EffectiveWeight = %d, want 1", target.EffectiveWeight.Load())
|
|
}
|
|
|
|
// 验证状态已记录
|
|
state := mgr.GetState(target.URL)
|
|
if state == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
if state.BaseWeight != 10 {
|
|
t.Errorf("BaseWeight = %d, want 10", state.BaseWeight)
|
|
}
|
|
}
|
|
|
|
func TestSlowStartManager_OnTargetUnhealthy(t *testing.T) {
|
|
mgr := NewSlowStartManager(100 * time.Millisecond)
|
|
|
|
target := &Target{
|
|
URL: "http://backend:8080",
|
|
Weight: 10,
|
|
SlowStart: 5 * time.Second,
|
|
}
|
|
|
|
mgr.OnTargetHealthy(target)
|
|
mgr.OnTargetUnhealthy(target)
|
|
|
|
// 验证状态已清除
|
|
state := mgr.GetState(target.URL)
|
|
if state != nil {
|
|
t.Error("state should be nil after unhealthy")
|
|
}
|
|
|
|
// 验证权重已重置
|
|
if target.EffectiveWeight.Load() != 0 {
|
|
t.Errorf("EffectiveWeight = %d, want 0", target.EffectiveWeight.Load())
|
|
}
|
|
}
|
|
|
|
func TestSlowStartManager_WeightProgression(t *testing.T) {
|
|
mgr := NewSlowStartManager(50 * time.Millisecond)
|
|
|
|
target := &Target{
|
|
URL: "http://backend:8080",
|
|
Weight: 10,
|
|
SlowStart: 200 * time.Millisecond,
|
|
}
|
|
|
|
var mu sync.Mutex
|
|
targets := map[string]*Target{target.URL: target}
|
|
mgr.SetFindTarget(func(url string) *Target {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
return targets[url]
|
|
})
|
|
|
|
mgr.OnTargetHealthy(target)
|
|
mgr.Start()
|
|
defer mgr.Stop()
|
|
|
|
// 等待慢启动完成
|
|
time.Sleep(300 * time.Millisecond)
|
|
|
|
// 验证权重已达到配置值或重置为 0
|
|
ew := target.EffectiveWeight.Load()
|
|
if ew != 0 && ew != int64(target.Weight) {
|
|
t.Errorf("EffectiveWeight = %d, want 0 or %d", ew, target.Weight)
|
|
}
|
|
}
|
|
|
|
func TestSlowStartManager_NoSlowStart(t *testing.T) {
|
|
mgr := NewSlowStartManager(100 * time.Millisecond)
|
|
|
|
target := &Target{
|
|
URL: "http://backend:8080",
|
|
Weight: 10,
|
|
SlowStart: 0, // 未配置慢启动
|
|
}
|
|
|
|
mgr.OnTargetHealthy(target)
|
|
|
|
// 验证没有设置有效权重
|
|
if target.EffectiveWeight.Load() != 0 {
|
|
t.Errorf("EffectiveWeight = %d, want 0 (no slow start)", target.EffectiveWeight.Load())
|
|
}
|
|
|
|
// 验证状态未记录
|
|
state := mgr.GetState(target.URL)
|
|
if state != nil {
|
|
t.Error("state should be nil when slow_start is 0")
|
|
}
|
|
}
|
|
|
|
func TestTarget_GetEffectiveWeight(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
weight int
|
|
effectiveWeight int64
|
|
want int
|
|
}{
|
|
{
|
|
name: "no slow start",
|
|
weight: 10,
|
|
effectiveWeight: 0,
|
|
want: 10,
|
|
},
|
|
{
|
|
name: "slow start in progress",
|
|
weight: 10,
|
|
effectiveWeight: 5,
|
|
want: 5,
|
|
},
|
|
{
|
|
name: "slow start at 1",
|
|
weight: 10,
|
|
effectiveWeight: 1,
|
|
want: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
target := &Target{
|
|
Weight: tt.weight,
|
|
}
|
|
target.EffectiveWeight.Store(tt.effectiveWeight)
|
|
|
|
got := target.GetEffectiveWeight()
|
|
if got != tt.want {
|
|
t.Errorf("GetEffectiveWeight() = %d, want %d", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSlowStartManager_GetAllStates(t *testing.T) {
|
|
mgr := NewSlowStartManager(100 * time.Millisecond)
|
|
|
|
target1 := &Target{
|
|
URL: "http://backend1:8080",
|
|
Weight: 10,
|
|
SlowStart: 5 * time.Second,
|
|
}
|
|
target2 := &Target{
|
|
URL: "http://backend2:8080",
|
|
Weight: 5,
|
|
SlowStart: 3 * time.Second,
|
|
}
|
|
|
|
mgr.OnTargetHealthy(target1)
|
|
mgr.OnTargetHealthy(target2)
|
|
|
|
states := mgr.GetAllStates()
|
|
if len(states) != 2 {
|
|
t.Errorf("len(states) = %d, want 2", len(states))
|
|
}
|
|
}
|