perf(网关): 实现上游账号连接池隔离

新增隔离策略与连接池缓存回收

连接池大小跟随账号并发并处理代理切换

同步配置默认值与示例并补充测试
This commit is contained in:
yangjianbo
2025-12-31 11:43:58 +08:00
parent 3d7f8e4b3a
commit d1c9889609
11 changed files with 790 additions and 104 deletions

View File

@@ -8,10 +8,21 @@ import (
"github.com/Wei-Shaw/sub2api/internal/config"
)
// httpClientSink 用于防止编译器优化掉基准测试中的赋值操作
// 这是 Go 基准测试的常见模式,确保测试结果准确
var httpClientSink *http.Client
// BenchmarkHTTPUpstreamProxyClient 对比重复创建与复用代理客户端的开销
// BenchmarkHTTPUpstreamProxyClient 对比重复创建与复用代理客户端的开销
//
// 测试目的:
// - 验证连接池复用相比每次新建的性能提升
// - 量化内存分配差异
//
// 预期结果:
// - "复用" 子测试应显著快于 "新建"
// - "复用" 子测试应零内存分配
func BenchmarkHTTPUpstreamProxyClient(b *testing.B) {
// 创建测试配置
cfg := &config.Config{
Gateway: config.GatewayConfig{ResponseHeaderTimeout: 300},
}
@@ -22,24 +33,33 @@ func BenchmarkHTTPUpstreamProxyClient(b *testing.B) {
}
proxyURL := "http://127.0.0.1:8080"
b.ReportAllocs()
b.ReportAllocs() // 报告内存分配统计
// 子测试:每次新建客户端
// 模拟未优化前的行为,每次请求都创建新的 http.Client
b.Run("新建", func(b *testing.B) {
parsedProxy, err := url.Parse(proxyURL)
if err != nil {
b.Fatalf("解析代理地址失败: %v", err)
}
settings := defaultPoolSettings(cfg)
for i := 0; i < b.N; i++ {
// 每次迭代都创建新客户端,包含 Transport 分配
httpClientSink = &http.Client{
Transport: buildUpstreamTransport(cfg, parsedProxy),
Transport: buildUpstreamTransport(settings, parsedProxy),
}
}
})
// 子测试:复用已缓存的客户端
// 模拟优化后的行为,从缓存获取客户端
b.Run("复用", func(b *testing.B) {
client := svc.getOrCreateClient(proxyURL)
b.ResetTimer()
// 预热:确保客户端已缓存
entry := svc.getOrCreateClient(proxyURL, 1, 1)
client := entry.client
b.ResetTimer() // 重置计时器,排除预热时间
for i := 0; i < b.N; i++ {
// 直接使用缓存的客户端,无内存分配
httpClientSink = client
}
})