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

199 lines
5.4 KiB
Markdown

# HTTP连接复用最佳实践
## 什么是连接复用?
HTTP连接复用(Connection Reuse)是指在同一个HTTP客户端实例中,对相同目标主机的多个请求复用已建立的TCP连接,而不是为每个请求都创建新的连接。这可以显著提高性能并减少资源消耗。
## 为什么需要连接复用?
1. **性能提升**:避免重复的TCP三次握手和TLS握手
2. **资源节约**:减少系统文件描述符和内存使用
3. **降低延迟**:复用已建立的连接减少连接建立时间
4. **服务器友好**:减少服务器连接压力
## curlx中的连接复用配置
### 基本配置参数
```go
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. 正确使用单例模式
```go
// ❌ 错误做法:每次请求都创建新客户端
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. 合理设置连接池大小
```go
// 根据应用场景调整配置
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. 监控连接池状态
```go
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()
}
}
```
## 性能对比测试
```go
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会话复用。
## 调试技巧
```go
// 启用详细的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. **负载均衡**:考虑使用连接池配合负载均衡
```go
// 生产环境推荐配置
func productionConfig() *Curlx {
return NewCurlx(
WithMaxIdleConns(200),
WithMaxIdleConnsPerHost(20),
WithMaxConnsPerHost(100),
WithIdleConnTimeout(120*time.Second),
SetOptionTimeOut(30*time.Second),
)
}
```