perf: 六项性能优化

- DNS 解析缓存:sync.Map 缓存避免重复系统调用
- 凭据测试 TCP 预检:不可达目标直接跳过全部凭据
- Web 探测 HTTP Client 复用:全局共享连接池
- 端口扫描 Bloom Filter 去重:替代 map 降低内存
- 进度条 atomic 累加 + 50ms 节流渲染:消除锁竞争
- 服务探针预解码:Init 时预编译,运行时零解码开销
This commit is contained in:
ZacharyZcR
2026-05-13 00:38:08 +08:00
parent 9092416485
commit af327ee944
28 changed files with 178 additions and 64 deletions
+1 -1
View File
@@ -391,7 +391,7 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string,
}
packets := make([]icmpPacket, 0, len(hostslist))
for _, host := range hostslist {
dst, _ := net.ResolveIPAddr("ip", host)
dst, _ := common.DNSCache.ResolveIP(host)
packets = append(packets, icmpPacket{data: makemsg(host), dst: dst})
}
+18 -10
View File
@@ -39,17 +39,22 @@ var resourceExhaustedPatterns = []string{
}
// resultCollector 结果收集器,用于并发安全地收集扫描结果
// 使用 map 实现:O(1) 的添加和删除,无顺序依赖问题
// 使用 Bloom Filter 去重 + slice 存储,大规模扫描时内存更优
type resultCollector struct {
mu sync.Mutex
addrs map[string]struct{}
addrs []string
bloom *BloomFilter
stream chan<- string // 可选:流式通知 channel
}
// newResultCollector 创建结果收集器
func newResultCollector(stream chan<- string) *resultCollector {
func newResultCollector(stream chan<- string, expectedSize int) *resultCollector {
if expectedSize < 1024 {
expectedSize = 1024
}
return &resultCollector{
addrs: make(map[string]struct{}),
addrs: make([]string, 0, expectedSize/10),
bloom: NewBloomFilter(expectedSize, 0.001),
stream: stream,
}
}
@@ -57,7 +62,12 @@ func newResultCollector(stream chan<- string) *resultCollector {
// Add 添加一个扫描结果
func (c *resultCollector) Add(addr string) {
c.mu.Lock()
c.addrs[addr] = struct{}{}
if c.bloom.Contains(addr) {
c.mu.Unlock()
return
}
c.bloom.Add(addr)
c.addrs = append(c.addrs, addr)
c.mu.Unlock()
if c.stream != nil {
c.stream <- addr
@@ -67,10 +77,8 @@ func (c *resultCollector) Add(addr string) {
// GetAll 获取所有结果
func (c *resultCollector) GetAll() []string {
c.mu.Lock()
result := make([]string, 0, len(c.addrs))
for addr := range c.addrs {
result = append(result, addr)
}
result := make([]string, len(c.addrs))
copy(result, c.addrs)
c.mu.Unlock()
return result
}
@@ -181,7 +189,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
to := time.Duration(timeout) * time.Second
adaptiveTO := NewAdaptiveTimeout(to)
var count int64
collector := newResultCollector(stream)
collector := newResultCollector(stream, totalTasks)
failedCollector := &failedPortCollector{}
var wg sync.WaitGroup
+17
View File
@@ -25,6 +25,23 @@ func (vs *VScan) Init() {
vs.parseProbesToMapKName()
vs.SetusedProbes()
vs.compileFallbacks() // 编译 fallback 数组
vs.preDecodeProbeData() // 预解码探针数据
}
// preDecodeProbeData 预解码所有探针的 Data 字段,避免运行时重复解码
func (vs *VScan) preDecodeProbeData() {
for i := range vs.Probes {
if vs.Probes[i].Data != "" {
decoded, err := DecodeData(vs.Probes[i].Data)
if err == nil {
vs.Probes[i].DecodedData = decoded
}
}
}
// 同步到 map
for i := range vs.Probes {
vs.ProbesMapKName[vs.Probes[i].Name] = vs.Probes[i]
}
}
// compileFallbacks 编译所有探测器的 fallback 数组
+1
View File
@@ -20,6 +20,7 @@ const MaxFallbacks = 20
type Probe struct {
Name string // 探测器名称
Data string // 探测数据
DecodedData []byte // 预解码的探测数据
Protocol string // 协议
Ports string // 端口范围
SSLPorts string // SSL端口范围
+26 -7
View File
@@ -219,9 +219,14 @@ func (s *SmartPortInfoScanner) tryProbeList(probes []*Probe, usedProbes map[stri
}
usedProbes[probe.Name] = struct{}{}
probeData, err := DecodeData(probe.Data)
if err != nil {
continue
// 优先使用预解码数据
probeData := probe.DecodedData
if probeData == nil {
var err error
probeData, err = DecodeData(probe.Data)
if err != nil {
continue
}
}
// 使用 TotalWaitMS 设置动态超时
@@ -282,8 +287,15 @@ func (s *SmartPortInfoScanner) performSSLSecondStage(serviceInfo *ServiceInfo) *
continue
}
probeData, err := DecodeData(probe.Data)
if err != nil || len(probeData) == 0 {
probeData := probe.DecodedData
if probeData == nil {
var decErr error
probeData, decErr = DecodeData(probe.Data)
if decErr != nil || len(probeData) == 0 {
continue
}
}
if len(probeData) == 0 {
continue
}
response := s.info.Connect(probeData)
@@ -317,8 +329,15 @@ func (s *SmartPortInfoScanner) tryHTTPSProbe() *ServiceInfo {
return nil
}
probeData, err := DecodeData(probe.Data)
if err != nil || len(probeData) == 0 {
probeData := probe.DecodedData
if probeData == nil {
var decErr error
probeData, decErr = DecodeData(probe.Data)
if decErr != nil || len(probeData) == 0 {
return nil
}
}
if len(probeData) == 0 {
return nil
}
response := s.info.Connect(probeData)
+21 -10
View File
@@ -19,6 +19,25 @@ import (
// Web服务检测
// ===============================
// 全局共享 HTTP Client,复用连接池减少 TLS 握手和 TCP 建连开销
var (
sharedHTTPClientOnce sync.Once
sharedHTTPClient *http.Client
)
func getSharedHTTPClient(config *common.Config) *http.Client {
sharedHTTPClientOnce.Do(func() {
sharedHTTPClient = createHTTPClient(config)
// 启用 keep-alive 复用连接
if t, ok := sharedHTTPClient.Transport.(*http.Transport); ok {
t.DisableKeepAlives = false
t.MaxIdleConns = 100
t.MaxIdleConnsPerHost = 2
}
})
return sharedHTTPClient
}
// WebPortDetector 简化的Web检测器 - 保持API兼容
type WebPortDetector struct{}
@@ -59,15 +78,7 @@ func DetectHTTPScheme(host string, port int, config *common.Config, session *com
// TLS握手失败,记录原因
// 第二步:尝试HTTP请求(回退检测HTTP)
client := &http.Client{
Timeout: timeout,
Transport: &http.Transport{
DisableKeepAlives: true,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // 不跟随重定向
},
}
client := getSharedHTTPClient(config)
// 使用HEAD请求(更轻量)
httpURL := fmt.Sprintf("http://%s", addr)
@@ -132,7 +143,7 @@ func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int, config *c
return false
}
client := createHTTPClient(config)
client := getSharedHTTPClient(config)
// 尝试HTTP
if w.tryHTTP(client, host, port, "http") {