perf(proxy): 缓存键哈希计算零分配优化

- 新增 buildCacheKeyHashValue 直接计算哈希值
- 消除缓存键字符串构建的内存分配
- 内部调用改用新函数降低 GC 压力
- 添加基准测试对比两种方法性能差异

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-16 11:09:27 +08:00
parent 9cbc380de5
commit eccdcde901
2 changed files with 52 additions and 3 deletions

View File

@ -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)

View File

@ -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
}
})
}