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+ 用例全通过。
116 lines
2.8 KiB
Go
116 lines
2.8 KiB
Go
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 双 EMA(lock-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())
|
||
}
|