5.4 KiB
5.4 KiB
HTTP连接复用最佳实践
什么是连接复用?
HTTP连接复用(Connection Reuse)是指在同一个HTTP客户端实例中,对相同目标主机的多个请求复用已建立的TCP连接,而不是为每个请求都创建新的连接。这可以显著提高性能并减少资源消耗。
为什么需要连接复用?
- 性能提升:避免重复的TCP三次握手和TLS握手
- 资源节约:减少系统文件描述符和内存使用
- 降低延迟:复用已建立的连接减少连接建立时间
- 服务器友好:减少服务器连接压力
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")
}
生产环境建议
- 预热连接:应用启动时进行连接预热
- 监控指标:监控连接池使用率和错误率
- 优雅关闭:应用关闭时清理连接资源
- 负载均衡:考虑使用连接池配合负载均衡
// 生产环境推荐配置
func productionConfig() *Curlx {
return NewCurlx(
WithMaxIdleConns(200),
WithMaxIdleConnsPerHost(20),
WithMaxConnsPerHost(100),
WithIdleConnTimeout(120*time.Second),
SetOptionTimeOut(30*time.Second),
)
}