mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
扫描前自动探测网络环境(RTT、丢包率、fd limit),基于探测数据 推导 6 个关键参数,替代硬编码默认值: - Timeout: median_RTT + 4σ(覆盖 99.9% 正常连接) - ModuleThreadNum: target_concurrency / 30 - MaxRetries: ceil(log(0.01)/log(loss_rate))(全失败概率 <1%) - ICMPRate: 环境基准 × fd 系数 - PocNum: 跟随 ModuleThreadNum - DisablePing: 已有 ICMP 权限降级机制 线程池从单信号(资源耗尽率)升级为 AIMD + 慢启动: - 慢启动:target/4 起步,500ms 翻倍 - 稳态 AIMD:健康 +5%,拥塞 ×0.5 - 双信号:资源耗尽率 + RTT 趋势(双 EMA) 用户 -t 显式指定时作为 ceiling,探测仍调整其他参数。 测试:单元 + 边界 + 集成 + 真实网络,core 包 580+ 用例全通过。
155 lines
3.4 KiB
Go
155 lines
3.4 KiB
Go
package core
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// newTestPool 测试辅助:创建测试用的自适应线程池
|
|
func newTestPool(t *testing.T, size int, fn func(interface{})) (*AdaptivePool, *ScanMetrics) {
|
|
t.Helper()
|
|
metrics := &ScanMetrics{}
|
|
pool, err := NewAdaptivePool(size, size, fn, metrics)
|
|
if err != nil {
|
|
t.Fatalf("创建线程池失败: %v", err)
|
|
}
|
|
return pool, metrics
|
|
}
|
|
|
|
// TestAdaptivePool_DowngradeOnHighExhaustion 验证资源耗尽率高时降低线程数
|
|
func TestAdaptivePool_DowngradeOnHighExhaustion(t *testing.T) {
|
|
pool, metrics := newTestPool(t, 100, func(interface{}) {})
|
|
defer pool.Release()
|
|
|
|
// 慢启动先跑到 target
|
|
pool.inSlowStart = false
|
|
pool.tune(100)
|
|
|
|
initialCap := pool.Cap()
|
|
|
|
// 模拟高资源耗尽率:20%
|
|
for i := 0; i < 200; i++ {
|
|
if i < 40 {
|
|
metrics.RecordExhausted()
|
|
} else {
|
|
metrics.RecordConnect(time.Millisecond)
|
|
}
|
|
}
|
|
|
|
// 触发调整
|
|
for i := 0; i < 20; i++ {
|
|
_ = pool.Invoke(nil)
|
|
time.Sleep(time.Millisecond * 30)
|
|
}
|
|
|
|
finalCap := pool.Cap()
|
|
|
|
if finalCap >= initialCap {
|
|
t.Errorf("应该降级: 初始 %d, 最终 %d", initialCap, finalCap)
|
|
}
|
|
|
|
if finalCap < 10 {
|
|
t.Errorf("降到 minSize 以下: %d", finalCap)
|
|
}
|
|
|
|
t.Logf("降级成功: %d -> %d", initialCap, finalCap)
|
|
}
|
|
|
|
// TestAdaptivePool_SlowStart 验证慢启动行为
|
|
func TestAdaptivePool_SlowStart(t *testing.T) {
|
|
metrics := &ScanMetrics{}
|
|
pool, err := NewAdaptivePool(100, 100, func(interface{}) {}, metrics)
|
|
if err != nil {
|
|
t.Fatalf("创建线程池失败: %v", err)
|
|
}
|
|
defer pool.Release()
|
|
|
|
// 初始应该是 target/4 = 25
|
|
initialCap := pool.Cap()
|
|
if initialCap > 30 {
|
|
t.Errorf("慢启动初始值应该 <= 30, got %d", initialCap)
|
|
}
|
|
|
|
if !pool.inSlowStart {
|
|
t.Error("应该处于慢启动状态")
|
|
}
|
|
|
|
t.Logf("慢启动初始: cap=%d, inSlowStart=%v", initialCap, pool.inSlowStart)
|
|
}
|
|
|
|
// TestAdaptivePool_MinSizeBoundary 验证不会降到 minSize 以下
|
|
func TestAdaptivePool_MinSizeBoundary(t *testing.T) {
|
|
pool, metrics := newTestPool(t, 40, func(interface{}) {})
|
|
defer pool.Release()
|
|
|
|
pool.inSlowStart = false
|
|
pool.tune(40)
|
|
|
|
// 极端耗尽
|
|
for i := 0; i < 500; i++ {
|
|
metrics.RecordExhausted()
|
|
}
|
|
|
|
for i := 0; i < 50; i++ {
|
|
_ = pool.Invoke(nil)
|
|
time.Sleep(time.Millisecond * 15)
|
|
}
|
|
|
|
finalCap := pool.Cap()
|
|
if finalCap < 10 {
|
|
t.Errorf("线程数 < 10: %d", finalCap)
|
|
}
|
|
|
|
t.Logf("最小边界测试通过: cap=%d", finalCap)
|
|
}
|
|
|
|
// TestAdaptivePool_NotEnoughSamples 验证样本不足时不调整
|
|
func TestAdaptivePool_NotEnoughSamples(t *testing.T) {
|
|
pool, metrics := newTestPool(t, 100, func(interface{}) {})
|
|
defer pool.Release()
|
|
|
|
pool.inSlowStart = false
|
|
pool.tune(100)
|
|
initialCap := pool.Cap()
|
|
|
|
// 只 20 个样本,不足 30 的阈值
|
|
for i := 0; i < 20; i++ {
|
|
metrics.RecordExhausted()
|
|
}
|
|
|
|
for i := 0; i < 10; i++ {
|
|
_ = pool.Invoke(nil)
|
|
}
|
|
time.Sleep(time.Millisecond * 50)
|
|
|
|
finalCap := pool.Cap()
|
|
if finalCap != initialCap {
|
|
t.Errorf("样本不足时不应该调整: %d -> %d", initialCap, finalCap)
|
|
}
|
|
}
|
|
|
|
// TestAdaptivePool_Wait 验证 Wait 方法
|
|
func TestAdaptivePool_Wait(t *testing.T) {
|
|
pool, _ := newTestPool(t, 10, func(interface{}) {
|
|
time.Sleep(time.Millisecond * 50)
|
|
})
|
|
defer pool.Release()
|
|
|
|
pool.inSlowStart = false
|
|
pool.tune(10)
|
|
|
|
for i := 0; i < 20; i++ {
|
|
_ = pool.Invoke(nil)
|
|
}
|
|
|
|
start := time.Now()
|
|
pool.Wait()
|
|
duration := time.Since(start)
|
|
|
|
if duration > 300*time.Millisecond {
|
|
t.Errorf("Wait 耗时过长: %v", duration)
|
|
}
|
|
|
|
t.Logf("Wait 测试通过: %v", duration)
|
|
}
|