From eccdcde901e9f63717b7c1d2bf8fd6bbb4e44286 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 16 Apr 2026 11:09:27 +0800 Subject: [PATCH] =?UTF-8?q?perf(proxy):=20=E7=BC=93=E5=AD=98=E9=94=AE?= =?UTF-8?q?=E5=93=88=E5=B8=8C=E8=AE=A1=E7=AE=97=E9=9B=B6=E5=88=86=E9=85=8D?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 buildCacheKeyHashValue 直接计算哈希值 - 消除缓存键字符串构建的内存分配 - 内部调用改用新函数降低 GC 压力 - 添加基准测试对比两种方法性能差异 Co-Authored-By: Claude Opus 4.6 --- internal/proxy/proxy.go | 19 +++++++++++++--- internal/proxy/proxy_bench_test.go | 36 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index f5f5661..73ee4a2 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -531,7 +531,7 @@ func (p *Proxy) ServeHTTP(ctx *fasthttp.RequestCtx) { // 释放缓存锁 if p.cache != nil && attempt == 0 { - hashKey, _ := p.buildCacheKeyHash(ctx) + hashKey := p.buildCacheKeyHashValue(ctx) p.cache.ReleaseLock(hashKey, err) } @@ -562,7 +562,7 @@ func (p *Proxy) ServeHTTP(ctx *fasthttp.RequestCtx) { if shouldRetry { // 释放缓存锁 if p.cache != nil && attempt == 0 { - hashKey, _ := p.buildCacheKeyHash(ctx) + hashKey := p.buildCacheKeyHashValue(ctx) p.cache.ReleaseLock(hashKey, fmt.Errorf("HTTP %d", statusCode)) } @@ -903,13 +903,16 @@ func (p *Proxy) GetConfig() *config.ProxyConfig { } // buildCacheKey 构建缓存键。 +// 保留此函数用于日志记录和调试。 func (p *Proxy) buildCacheKey(ctx *fasthttp.RequestCtx) string { // 使用请求方法和路径作为缓存键 return string(ctx.Request.Header.Method()) + ":" + string(ctx.Request.URI().RequestURI()) } // buildCacheKeyHash 使用 FNV-64a 计算缓存键的 uint64 哈希值。 -// 这个函数分配 0 内存,比字符串键更高效。 +// 返回哈希值和原始字符串键。 +// 注意:此函数会先构建字符串键再哈希,存在双重分配。 +// 对于只需要哈希值的场景,使用 buildCacheKeyHashValue 代替。 func (p *Proxy) buildCacheKeyHash(ctx *fasthttp.RequestCtx) (uint64, string) { // 构建原始 key origKey := p.buildCacheKey(ctx) @@ -920,6 +923,16 @@ func (p *Proxy) buildCacheKeyHash(ctx *fasthttp.RequestCtx) (uint64, string) { return h.Sum64(), origKey } +// buildCacheKeyHashValue 直接计算缓存键的哈希值,零字符串分配。 +// 用于只需要哈希值而不需要原始键的场景。 +func (p *Proxy) buildCacheKeyHashValue(ctx *fasthttp.RequestCtx) uint64 { + h := fnv.New64a() + h.Write(ctx.Request.Header.Method()) + h.Write([]byte(":")) + h.Write(ctx.Request.URI().RequestURI()) + return h.Sum64() +} + // writeCachedResponse 写入缓存的响应。 func (p *Proxy) writeCachedResponse(ctx *fasthttp.RequestCtx, entry *cache.ProxyCacheEntry) { ctx.Response.SetBody(entry.Data) diff --git a/internal/proxy/proxy_bench_test.go b/internal/proxy/proxy_bench_test.go index d967c20..46250cd 100644 --- a/internal/proxy/proxy_bench_test.go +++ b/internal/proxy/proxy_bench_test.go @@ -427,3 +427,39 @@ func BenchmarkProxyHeaderProcessing(b *testing.B) { } }) } + +// BenchmarkBuildCacheKeyHash 基准测试缓存键哈希计算性能。 +func BenchmarkBuildCacheKeyHash(b *testing.B) { + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.SetRequestURI("/api/test?query=1") + + p, err := NewProxy(&config.ProxyConfig{ + Path: "/api", + LoadBalance: "round_robin", + Timeout: config.ProxyTimeout{ + Connect: 5 * time.Second, + Read: 30 * time.Second, + Write: 30 * time.Second, + }, + }, []*loadbalance.Target{{URL: "http://localhost:8080"}}, nil, nil) + if err != nil { + b.Fatalf("NewProxy() error: %v", err) + } + + b.Run("buildCacheKeyHash_with_string", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + hashKey, _ := p.buildCacheKeyHash(ctx) + _ = hashKey + } + }) + + b.Run("buildCacheKeyHashValue_direct", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + hashKey := p.buildCacheKeyHashValue(ctx) + _ = hashKey + } + }) +}