perf: 使用单例模式消除重复协议检测

核心修复:
- 将WebPortDetector改为全局单例,消除多实例问题
- 所有协议检测共享同一个缓存,避免重复TCP连接
- 使用sync.Once确保线程安全的单例初始化

性能提升:
- 每个端口的协议检测从多次减少到1次
- 大幅降低TCP连接数,减少网络开销
- 缓存命中率显著提升

技术实现:
- GetWebPortDetector() 替代 NewWebPortDetector()
- newWebPortDetector() 改为私有方法
- BaseScanStrategy统一使用单例实例

这是数据结构决定性能的经典案例 - 通过正确的实例管理
彻底解决了重复检测问题
This commit is contained in:
ZacharyZcR 2025-09-02 00:06:17 +00:00
parent 57aa48be58
commit 9ad397f58f
5 changed files with 45 additions and 6 deletions

View File

@ -194,15 +194,15 @@ func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool {
// isWebServicePort 使用智能检测判断端口是否运行Web服务含预定义端口 // isWebServicePort 使用智能检测判断端口是否运行Web服务含预定义端口
func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool { func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool {
// 创建Web端口检测器实例 // 使用全局单例Web端口检测器
detector := NewWebPortDetector() detector := GetWebPortDetector()
return detector.IsWebService(host, port) return detector.IsWebService(host, port)
} }
// isWebServicePortByProtocol 仅通过HTTP协议检测判断端口是否运行Web服务不使用预定义端口列表 // isWebServicePortByProtocol 仅通过HTTP协议检测判断端口是否运行Web服务不使用预定义端口列表
func (b *BaseScanStrategy) isWebServicePortByProtocol(host string, port int) bool { func (b *BaseScanStrategy) isWebServicePortByProtocol(host string, port int) bool {
// 创建Web端口检测器实例 // 使用全局单例Web端口检测器
detector := NewWebPortDetector() detector := GetWebPortDetector()
// 直接调用协议检测,跳过预定义端口检查 // 直接调用协议检测,跳过预定义端口检查
return detector.DetectHTTPServiceOnly(host, port) return detector.DetectHTTPServiceOnly(host, port)
} }

View File

@ -25,12 +25,28 @@ type WebPortDetector struct {
httpsClient *http.Client httpsClient *http.Client
// 检测结果缓存(避免重复检测) // 检测结果缓存(避免重复检测)
detectionCache map[string]bool detectionCache map[string]bool
// 协议检测缓存避免重复TCP连接
protocolCache map[string]bool
// 缓存互斥锁 // 缓存互斥锁
cacheMutex sync.RWMutex cacheMutex sync.RWMutex
} }
// NewWebPortDetector 创建Web端口检测器 var (
func NewWebPortDetector() *WebPortDetector { // 全局单例实例
webDetectorInstance *WebPortDetector
webDetectorOnce sync.Once
)
// GetWebPortDetector 获取Web端口检测器单例
func GetWebPortDetector() *WebPortDetector {
webDetectorOnce.Do(func() {
webDetectorInstance = newWebPortDetector()
})
return webDetectorInstance
}
// newWebPortDetector 创建Web端口检测器私有方法
func newWebPortDetector() *WebPortDetector {
timeout := 3 * time.Second timeout := 3 * time.Second
// 只保留最基本的Web端口用于快速路径优化 // 只保留最基本的Web端口用于快速路径优化
@ -82,6 +98,7 @@ func NewWebPortDetector() *WebPortDetector {
httpClient: httpClient, httpClient: httpClient,
httpsClient: httpsClient, httpsClient: httpsClient,
detectionCache: make(map[string]bool), detectionCache: make(map[string]bool),
protocolCache: make(map[string]bool),
} }
} }
@ -350,6 +367,28 @@ func (w *WebPortDetector) isNonWebHTTPService(serverHeader, contentType string)
// quickProtocolCheck 快速协议检查避免向非HTTP服务发送HTTP请求 // quickProtocolCheck 快速协议检查避免向非HTTP服务发送HTTP请求
func (w *WebPortDetector) quickProtocolCheck(ctx context.Context, host string, port int) bool { func (w *WebPortDetector) quickProtocolCheck(ctx context.Context, host string, port int) bool {
// 检查协议缓存
protocolKey := fmt.Sprintf("%s:%d:protocol", host, port)
w.cacheMutex.RLock()
if cached, exists := w.protocolCache[protocolKey]; exists {
w.cacheMutex.RUnlock()
return cached
}
w.cacheMutex.RUnlock()
// 执行实际的协议检测
result := w.doProtocolCheck(ctx, host, port)
// 缓存结果
w.cacheMutex.Lock()
w.protocolCache[protocolKey] = result
w.cacheMutex.Unlock()
return result
}
// doProtocolCheck 执行实际的协议检测
func (w *WebPortDetector) doProtocolCheck(ctx context.Context, host string, port int) bool {
// 建立TCP连接 // 建立TCP连接
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 2*time.Second) conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 2*time.Second)
if err != nil { if err != nil {

BIN
fscan-lite/main.o Normal file

Binary file not shown.

BIN
fscan-lite/platform.o Normal file

Binary file not shown.

BIN
fscan-lite/scanner.o Normal file

Binary file not shown.