Files
fscan/core/scan_metrics.go
ZacharyZcR 8402be98e3 优化自适应扫描系统 & 修复 POC 调度问题
自适应扫描优化:
- target/ceiling 分离,自适应池可向上探索而非锁死在 target
- assessHealth 阈值按网络环境区分(LAN 收紧 / Internet 放宽)
- RTT 漂移时动态压低 target,配合 AIMD 双重降速
- 去掉 semaphore 双层流控,由 ants pool 统一反压
- 探测端口从 3 个扩充到 8 个,减少 RTT 采样偏差
- computeRetries 按环境调整目标概率和上限

Bug 修复:
- AdaptivePool.Wait() 加 10 分钟超时,防止 goroutine 卡死时永久挂起
- CEL 环境初始化失败后允许重试(sync.Once → sync.Mutex + 标志位)
- CAS 自旋加 runtime.Gosched() 退避,减少高并发下 CPU 空转
- -full 模式下 web 插件跳过 IsMarkedWebService 检查 #588
- 不确定服务补做 HTTP 回退探测,覆盖自定义框架漏网场景
- POC sets 纯字面量值跳过 CEL 编译,消除大量误报错误日志
2026-06-14 22:23:48 +08:00

119 lines
2.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package core
import (
"runtime"
"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
}
runtime.Gosched()
continue
}
next := old + (sample-old)/divisor
if target.CompareAndSwap(old, next) {
return
}
runtime.Gosched()
}
}
// 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())
}