mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
feat: 自适应并发调度 — 网络探测 + AIMD + 参数智能推导
扫描前自动探测网络环境(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+ 用例全通过。
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// 单元测试:ScanMetrics 基本操作
|
||||
// =============================================================================
|
||||
|
||||
func TestScanMetrics_Counters(t *testing.T) {
|
||||
m := &ScanMetrics{}
|
||||
|
||||
m.RecordConnect(time.Millisecond)
|
||||
m.RecordConnect(2 * time.Millisecond)
|
||||
m.RecordRefused(500 * time.Microsecond)
|
||||
m.RecordTimeout()
|
||||
m.RecordExhausted()
|
||||
|
||||
if m.Total() != 5 {
|
||||
t.Errorf("Total() = %d, want 5", m.Total())
|
||||
}
|
||||
|
||||
snap := m.Snapshot()
|
||||
if snap.Connects != 2 {
|
||||
t.Errorf("Connects = %d, want 2", snap.Connects)
|
||||
}
|
||||
if snap.Refused != 1 {
|
||||
t.Errorf("Refused = %d, want 1", snap.Refused)
|
||||
}
|
||||
if snap.Timeouts != 1 {
|
||||
t.Errorf("Timeouts = %d, want 1", snap.Timeouts)
|
||||
}
|
||||
if snap.Exhausted != 1 {
|
||||
t.Errorf("Exhausted = %d, want 1", snap.Exhausted)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 单元测试:RTT EMA 收敛
|
||||
// =============================================================================
|
||||
|
||||
func TestScanMetrics_RTT_EMA(t *testing.T) {
|
||||
m := &ScanMetrics{}
|
||||
|
||||
// 喂入稳定的 10ms RTT
|
||||
for i := 0; i < 100; i++ {
|
||||
m.RecordConnect(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
fast := m.RTTFast()
|
||||
if fast < 9*time.Millisecond || fast > 11*time.Millisecond {
|
||||
t.Errorf("稳定 10ms 后 RTTFast = %v, 应该接近 10ms", fast)
|
||||
}
|
||||
|
||||
ratio := m.RTTRatio()
|
||||
if ratio < 0.9 || ratio > 1.1 {
|
||||
t.Errorf("稳定状态 RTTRatio = %.2f, 应该接近 1.0", ratio)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanMetrics_RTT_Trend(t *testing.T) {
|
||||
m := &ScanMetrics{}
|
||||
|
||||
// 先喂入 100 个 5ms 建立基线
|
||||
for i := 0; i < 100; i++ {
|
||||
m.RecordConnect(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 再喂入 50 个 50ms(RTT 突增 10 倍)
|
||||
for i := 0; i < 50; i++ {
|
||||
m.RecordConnect(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
ratio := m.RTTRatio()
|
||||
// fast EMA 应该比 slow EMA 高(fast 跟踪快,slow 还没追上来)
|
||||
if ratio <= 1.0 {
|
||||
t.Errorf("RTT 突增后 RTTRatio = %.2f, 应该 > 1.0", ratio)
|
||||
}
|
||||
|
||||
t.Logf("RTT 突增后: ratio=%.2f, fast=%v", ratio, m.RTTFast())
|
||||
}
|
||||
|
||||
func TestScanMetrics_RTT_InsufficientSamples(t *testing.T) {
|
||||
m := &ScanMetrics{}
|
||||
|
||||
// 少于 20 个样本
|
||||
for i := 0; i < 10; i++ {
|
||||
m.RecordConnect(time.Millisecond)
|
||||
}
|
||||
|
||||
ratio := m.RTTRatio()
|
||||
if ratio != 1.0 {
|
||||
t.Errorf("样本不足时 RTTRatio = %.2f, 应该是 1.0", ratio)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 并发安全测试
|
||||
// =============================================================================
|
||||
|
||||
func TestScanMetrics_ConcurrentSafety(t *testing.T) {
|
||||
m := &ScanMetrics{}
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(4)
|
||||
go func() { defer wg.Done(); m.RecordConnect(time.Millisecond) }()
|
||||
go func() { defer wg.Done(); m.RecordRefused(time.Millisecond) }()
|
||||
go func() { defer wg.Done(); m.RecordTimeout() }()
|
||||
go func() { defer wg.Done(); m.RecordExhausted() }()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if m.Total() != 400 {
|
||||
t.Errorf("并发后 Total() = %d, want 400", m.Total())
|
||||
}
|
||||
|
||||
// 验证 Snapshot 不 panic
|
||||
snap := m.Snapshot()
|
||||
if snap.Total() != 400 {
|
||||
t.Errorf("并发后 Snapshot.Total() = %d, want 400", snap.Total())
|
||||
}
|
||||
|
||||
// 验证 RTTRatio 不 panic
|
||||
_ = m.RTTRatio()
|
||||
}
|
||||
Reference in New Issue
Block a user