Files
curlx/example/CONNECTION_REUSE.md
T
2026-03-01 23:10:09 +08:00

5.4 KiB

HTTP连接复用最佳实践

什么是连接复用?

HTTP连接复用(Connection Reuse)是指在同一个HTTP客户端实例中,对相同目标主机的多个请求复用已建立的TCP连接,而不是为每个请求都创建新的连接。这可以显著提高性能并减少资源消耗。

为什么需要连接复用?

  1. 性能提升:避免重复的TCP三次握手和TLS握手
  2. 资源节约:减少系统文件描述符和内存使用
  3. 降低延迟:复用已建立的连接减少连接建立时间
  4. 服务器友好:减少服务器连接压力

curlx中的连接复用配置

基本配置参数

client := NewCurlx(
    // 连接池大小配置
    WithMaxIdleConns(100),        // 总空闲连接数上限
    WithMaxIdleConnsPerHost(10),  // 每个主机的空闲连接数
    WithMaxConnsPerHost(50),      // 每个主机的最大连接数
    WithIdleConnTimeout(90*time.Second), // 空闲连接超时时间
    
    // 其他优化配置
    SetOptionTimeOut(30*time.Second),
)

参数详解

参数 默认值 说明
MaxIdleConns 100 连接池中保持的最大空闲连接总数
MaxIdleConnsPerHost 10 对每个主机保持的最大空闲连接数
MaxConnsPerHost 50 对每个主机允许的最大并发连接数
IdleConnTimeout 90s 空闲连接的超时时间

最佳实践

1. 正确使用单例模式

// ❌ 错误做法:每次请求都创建新客户端
func badExample() {
    for i := 0; i < 100; i++ {
        client := NewCurlx() // 每次都新建,无法复用连接
        client.Get(context.Background(), "https://example.com")
    }
}

// ✅ 正确做法:复用客户端实例
func goodExample() {
    client := NewCurlx( // 只创建一次
        WithMaxIdleConns(50),
        WithMaxIdleConnsPerHost(5),
    )
    
    for i := 0; i < 100; i++ {
        client.Get(context.Background(), "https://example.com") // 复用连接
    }
}

2. 合理设置连接池大小

// 根据应用场景调整配置
func getConfigForScenario(scenario string) []Option {
    switch scenario {
    case "high_concurrency":
        return []Option{
            WithMaxIdleConns(200),
            WithMaxIdleConnsPerHost(20),
            WithMaxConnsPerHost(100),
        }
    case "low_resource":
        return []Option{
            WithMaxIdleConns(20),
            WithMaxIdleConnsPerHost(2),
            WithMaxConnsPerHost(10),
        }
    default:
        return []Option{
            WithMaxIdleConns(100),
            WithMaxIdleConnsPerHost(10),
            WithMaxConnsPerHost(50),
        }
    }
}

3. 监控连接池状态

func monitorConnectionPool(client *Curlx) {
    manager := &ConnectionPoolManager{
        client: client, 
        transport: client.transport,
    }
    
    // 定期检查连接池状态
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        manager.PrintPoolStats()
    }
}

性能对比测试

func BenchmarkConnectionReuse(b *testing.B) {
    client := NewCurlx(
        WithMaxIdleConns(50),
        WithMaxIdleConnsPerHost(10),
    )
    
    b.Run("with_reuse", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            client.Get(context.Background(), "https://httpbin.org/get")
        }
    })
}

func BenchmarkWithoutReuse(b *testing.B) {
    b.Run("without_reuse", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            client := NewCurlx() // 每次新建客户端
            client.Get(context.Background(), "https://httpbin.org/get")
        }
    })
}

常见问题解答

Q: 连接池满了怎么办?

A: 当连接池满时,新的请求会等待空闲连接。可以通过增加MaxConnsPerHost来缓解。

Q: 如何清理空闲连接?

A: 空闲连接会在IdleConnTimeout后自动关闭,也可以手动调用transport.CloseIdleConnections()

Q: 不同主机的连接是否共享?

A: 不同主机的连接是隔离的,每个主机维护自己的连接池。

Q: HTTPS连接也能复用吗?

A: 是的,HTTPS连接同样支持复用,包括TLS会话复用。

调试技巧

// 启用详细的HTTP跟踪
import "net/http/httptrace"

func debugWithTrace() {
    trace := &httptrace.ClientTrace{
        GotConn: func(info httptrace.GotConnInfo) {
            fmt.Printf("连接复用: %v, 来自空闲池: %v\n", 
                info.Reused, info.WasIdle)
        },
        ConnectStart: func(network, addr string) {
            fmt.Printf("开始连接: %s %s\n", network, addr)
        },
        ConnectDone: func(network, addr string, err error) {
            if err != nil {
                fmt.Printf("连接完成: %v\n", err)
            }
        },
    }
    
    ctx := httptrace.WithClientTrace(context.Background(), trace)
    client := NewCurlx()
    client.Get(ctx, "https://httpbin.org/get")
}

生产环境建议

  1. 预热连接:应用启动时进行连接预热
  2. 监控指标:监控连接池使用率和错误率
  3. 优雅关闭:应用关闭时清理连接资源
  4. 负载均衡:考虑使用连接池配合负载均衡
// 生产环境推荐配置
func productionConfig() *Curlx {
    return NewCurlx(
        WithMaxIdleConns(200),
        WithMaxIdleConnsPerHost(20),
        WithMaxConnsPerHost(100),
        WithIdleConnTimeout(120*time.Second),
        SetOptionTimeOut(30*time.Second),
    )
}