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:
ZacharyZcR
2026-06-14 22:23:45 +08:00
parent e0468ecd35
commit 88c7e4f2be
21 changed files with 3287 additions and 251 deletions
+115
View File
@@ -0,0 +1,115 @@
package core
import (
"sync/atomic"
"time"
)
// ScanMetrics 扫描过程中的实时度量指标
// 所有方法均无锁,使用 atomic 操作,可在高并发下安全调用
type ScanMetrics struct {
connects atomic.Int64 // TCP 连接成功(端口开放)
refused atomic.Int64 // 连接被拒绝(端口关闭,快速 RTT)
timeouts atomic.Int64 // 连接超时(端口过滤/不可达)
exhausted atomic.Int64 // 资源耗尽(fd/端口/内存不足)
// RTT 追踪:双 EMA(指数移动平均)
// fast EMA (α=0.1) 跟踪近期趋势
// slow EMA (α=0.02) 作为基线参考
rttFastNs atomic.Int64 // 纳秒
rttSlowNs atomic.Int64 // 纳秒
rttSamples atomic.Int64
}
func (m *ScanMetrics) RecordConnect(rtt time.Duration) {
m.connects.Add(1)
m.recordRTT(rtt)
}
func (m *ScanMetrics) RecordRefused(rtt time.Duration) {
m.refused.Add(1)
m.recordRTT(rtt)
}
func (m *ScanMetrics) RecordTimeout() { m.timeouts.Add(1) }
func (m *ScanMetrics) RecordExhausted() { m.exhausted.Add(1) }
// recordRTT 更新 RTT 双 EMAlock-free CAS
func (m *ScanMetrics) recordRTT(rtt time.Duration) {
ns := int64(rtt)
if ns <= 0 {
return
}
m.rttSamples.Add(1)
// Fast EMA: α = 0.1 → new = old + (sample - old) / 10
updateEMA(&m.rttFastNs, ns, 10)
// Slow EMA: α = 0.02 → new = old + (sample - old) / 50
updateEMA(&m.rttSlowNs, ns, 50)
}
func updateEMA(target *atomic.Int64, sample int64, divisor int64) {
for {
old := target.Load()
if old == 0 {
if target.CompareAndSwap(0, sample) {
return
}
continue
}
next := old + (sample-old)/divisor
if target.CompareAndSwap(old, next) {
return
}
}
}
// Total 总操作数
func (m *ScanMetrics) Total() int64 {
return m.connects.Load() + m.refused.Load() + m.timeouts.Load() + m.exhausted.Load()
}
// MetricsSnapshot 度量快照,用于计算窗口内增量
type MetricsSnapshot struct {
Connects int64
Refused int64
Timeouts int64
Exhausted int64
RTTFastNs int64
RTTSlowNs int64
}
func (s MetricsSnapshot) Total() int64 {
return s.Connects + s.Refused + s.Timeouts + s.Exhausted
}
func (m *ScanMetrics) Snapshot() MetricsSnapshot {
return MetricsSnapshot{
Connects: m.connects.Load(),
Refused: m.refused.Load(),
Timeouts: m.timeouts.Load(),
Exhausted: m.exhausted.Load(),
RTTFastNs: m.rttFastNs.Load(),
RTTSlowNs: m.rttSlowNs.Load(),
}
}
// RTTRatio 返回 fast/slow EMA 的比值
// > 1.0 表示延迟在上升(拥塞信号),< 1.0 表示延迟在下降
// 样本不足时返回 1.0
func (m *ScanMetrics) RTTRatio() float64 {
if m.rttSamples.Load() < 20 {
return 1.0
}
fast := m.rttFastNs.Load()
slow := m.rttSlowNs.Load()
if slow <= 0 {
return 1.0
}
return float64(fast) / float64(slow)
}
// RTTFast 返回快速 EMA 值
func (m *ScanMetrics) RTTFast() time.Duration {
return time.Duration(m.rttFastNs.Load())
}