一些example
This commit is contained in:
@@ -0,0 +1,199 @@
|
|||||||
|
# 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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package example
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/yuninks/curlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionPoolManager 连接池管理器
|
||||||
|
type ConnectionPoolManager struct {
|
||||||
|
client *curlx.Curlx
|
||||||
|
transport *http.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnectionPoolManager 创建连接池管理器
|
||||||
|
func NewConnectionPoolManager(opts ...curlx.Option) *ConnectionPoolManager {
|
||||||
|
// 添加连接池优化配置
|
||||||
|
poolOpts := append(opts,
|
||||||
|
WithConnectionPoolSettings(
|
||||||
|
100, // MaxIdleConns
|
||||||
|
10, // MaxIdleConnsPerHost
|
||||||
|
50, // MaxConnsPerHost
|
||||||
|
90*time.Second, // IdleConnTimeout
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
client := curlx.NewCurlx(poolOpts...)
|
||||||
|
|
||||||
|
return &ConnectionPoolManager{
|
||||||
|
client: client,
|
||||||
|
// transport: client.transport,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConnectionPoolSettings 连接池配置选项
|
||||||
|
func WithConnectionPoolSettings(
|
||||||
|
maxIdleConns int,
|
||||||
|
maxIdleConnsPerHost int,
|
||||||
|
maxConnsPerHost int,
|
||||||
|
idleConnTimeout time.Duration,
|
||||||
|
) curlx.Option {
|
||||||
|
return func(options *curlx.ClientOptions) {
|
||||||
|
options.MaxIdleConns = maxIdleConns
|
||||||
|
options.MaxIdleConnsPerHost = maxIdleConnsPerHost
|
||||||
|
options.MaxConnsPerHost = maxConnsPerHost
|
||||||
|
options.IdleConnTimeout = idleConnTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConcurrentRequests 并发请求演示
|
||||||
|
func (cpm *ConnectionPoolManager) ConcurrentRequests(urls []string) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
results := make(chan string, len(urls))
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// 并发执行多个请求
|
||||||
|
for i, url := range urls {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int, targetURL string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
response, err := cpm.client.Get(ctx, targetURL)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
results <- fmt.Sprintf("Request %d to %s failed: %v (took %v)",
|
||||||
|
index, targetURL, err, duration)
|
||||||
|
} else {
|
||||||
|
results <- fmt.Sprintf("Request %d to %s succeeded: %d bytes (took %v)",
|
||||||
|
index, targetURL, len(response), duration)
|
||||||
|
}
|
||||||
|
}(i, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有请求完成
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
|
||||||
|
totalDuration := time.Since(startTime)
|
||||||
|
fmt.Printf("=== 并发请求完成 ===\n")
|
||||||
|
fmt.Printf("总耗时: %v\n", totalDuration)
|
||||||
|
fmt.Printf("平均每个请求: %v\n", totalDuration/time.Duration(len(urls)))
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
for result := range results {
|
||||||
|
fmt.Println(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出连接池状态
|
||||||
|
cpm.PrintPoolStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintPoolStats 打印连接池统计信息
|
||||||
|
func (cpm *ConnectionPoolManager) PrintPoolStats() {
|
||||||
|
// stats := cpm.transport
|
||||||
|
|
||||||
|
fmt.Printf("\n=== 连接池统计 ===\n")
|
||||||
|
// fmt.Printf("当前空闲连接数: %d\n", stats.IdleConnCount())
|
||||||
|
// fmt.Printf("总连接数: %d\n", stats.TotalConnCount())
|
||||||
|
// fmt.Printf("等待队列长度: %d\n", stats.WaitQueueLength())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReuseExample 连接复用示例
|
||||||
|
func (cpm *ConnectionPoolManager) ReuseExample(baseURL string, requestCount int) {
|
||||||
|
fmt.Printf("=== 连接复用测试 ===\n")
|
||||||
|
fmt.Printf("目标URL: %s\n", baseURL)
|
||||||
|
fmt.Printf("请求次数: %d\n\n", requestCount)
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
for i := 0; i < requestCount; i++ {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
response, err := cpm.client.Get(ctx, baseURL)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
cancel() // 释放context资源
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("第%d次请求失败: %v (耗时: %v)\n", i+1, err, duration)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("第%d次请求成功: %d字节 (耗时: %v)\n", i+1, len(response), duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小间隔避免请求过于密集
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDuration := time.Since(startTime)
|
||||||
|
fmt.Printf("\n=== 测试完成 ===\n")
|
||||||
|
fmt.Printf("总耗时: %v\n", totalDuration)
|
||||||
|
fmt.Printf("平均每请求: %v\n", totalDuration/time.Duration(requestCount))
|
||||||
|
|
||||||
|
cpm.PrintPoolStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistentConnectionExample 持久连接示例
|
||||||
|
func (cpm *ConnectionPoolManager) PersistentConnectionExample(targetURL string) {
|
||||||
|
fmt.Printf("=== 持久连接测试 ===\n")
|
||||||
|
fmt.Printf("测试URL: %s\n\n", targetURL)
|
||||||
|
|
||||||
|
// 预热连接
|
||||||
|
fmt.Println("预热阶段 - 建立初始连接...")
|
||||||
|
ctx1, cancel1 := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
_, err := cpm.client.Get(ctx1, targetURL)
|
||||||
|
cancel1()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("预热失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cpm.PrintPoolStats()
|
||||||
|
|
||||||
|
// 实际测试
|
||||||
|
fmt.Println("\n实际测试阶段...")
|
||||||
|
testStart := time.Now()
|
||||||
|
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
response, err := cpm.client.Get(ctx, targetURL)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("第%d次请求: 失败 (%v) - 耗时: %v\n", i, err, duration)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("第%d次请求: 成功 (%d字节) - 耗时: %v\n", i, len(response), duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTime := time.Since(testStart)
|
||||||
|
fmt.Printf("\n持久连接测试完成,总耗时: %v\n", totalTime)
|
||||||
|
cpm.PrintPoolStats()
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
package example
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/yuninks/curlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConnectionReuse(t *testing.T) {
|
||||||
|
// 创建带优化连接池配置的客户端
|
||||||
|
client := curlx.NewCurlx(
|
||||||
|
curlx.WithMaxIdleConns(50),
|
||||||
|
curlx.WithMaxIdleConnsPerHost(10),
|
||||||
|
curlx.WithMaxConnsPerHost(20),
|
||||||
|
curlx.WithIdleConnTimeout(60*time.Second),
|
||||||
|
curlx.WithOptionTimeOut(30*time.Second),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 测试同一个主机的多次请求,观察连接复用效果
|
||||||
|
targetURL := "https://httpbin.org/get"
|
||||||
|
|
||||||
|
fmt.Println("=== 连接复用测试开始 ===")
|
||||||
|
|
||||||
|
// 预热连接
|
||||||
|
fmt.Println("1. 预热连接...")
|
||||||
|
ctx1, cancel1 := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
_, err := client.Get(ctx1, targetURL)
|
||||||
|
cancel1()
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("预热请求失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(" 预热完成")
|
||||||
|
|
||||||
|
// 连续请求测试
|
||||||
|
fmt.Println("\n2. 连续请求测试...")
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
start := time.Now()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
|
||||||
|
response, err := client.Get(ctx, targetURL)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("第%d次请求失败: %v (耗时: %v)", i, err, duration)
|
||||||
|
} else {
|
||||||
|
t.Logf("第%d次请求成功: %d字节 (耗时: %v)", i, len(response), duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(200 * time.Millisecond) // 短暂间隔
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== 测试完成 ===")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConcurrentConnectionReuse(t *testing.T) {
|
||||||
|
// 创建连接池管理器
|
||||||
|
poolManager := NewConnectionPoolManager(
|
||||||
|
curlx.WithMaxIdleConns(100),
|
||||||
|
curlx.WithMaxIdleConnsPerHost(20),
|
||||||
|
curlx.WithMaxConnsPerHost(30),
|
||||||
|
curlx.WithIdleConnTimeout(120*time.Second),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 测试并发请求
|
||||||
|
urls := []string{
|
||||||
|
"https://httpbin.org/get",
|
||||||
|
"https://httpbin.org/uuid",
|
||||||
|
"https://httpbin.org/user-agent",
|
||||||
|
"https://httpbin.org/headers",
|
||||||
|
"https://httpbin.org/ip",
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("=== 并发连接复用测试 ===")
|
||||||
|
poolManager.ConcurrentRequests(urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersistentConnection(t *testing.T) {
|
||||||
|
// 创建优化的客户端
|
||||||
|
client := curlx.NewCurlx(
|
||||||
|
curlx.WithMaxIdleConns(30),
|
||||||
|
curlx.WithMaxIdleConnsPerHost(5),
|
||||||
|
curlx.WithMaxConnsPerHost(15),
|
||||||
|
curlx.WithIdleConnTimeout(30*time.Second),
|
||||||
|
)
|
||||||
|
|
||||||
|
manager := &ConnectionPoolManager{
|
||||||
|
client: client,
|
||||||
|
// transport: client.transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("=== 持久连接测试 ===")
|
||||||
|
manager.PersistentConnectionExample("https://httpbin.org/delay/1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_connectionReuse() {
|
||||||
|
// 最佳实践示例:如何正确配置连接复用
|
||||||
|
|
||||||
|
// 1. 创建优化配置的客户端
|
||||||
|
client := curlx.NewCurlx(
|
||||||
|
// 连接池配置
|
||||||
|
curlx.WithMaxIdleConns(100), // 总空闲连接数
|
||||||
|
curlx.WithMaxIdleConnsPerHost(10), // 每主机空闲连接数
|
||||||
|
curlx.WithMaxConnsPerHost(50), // 每主机最大连接数
|
||||||
|
curlx.WithIdleConnTimeout(90*time.Second), // 空闲超时时间
|
||||||
|
|
||||||
|
// 其他优化配置
|
||||||
|
curlx.WithOptionTimeOut(30*time.Second),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2. 复用同一个客户端实例进行多次请求
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 第一次请求会建立新连接
|
||||||
|
response1, err := client.Get(ctx, "https://httpbin.org/get")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("首次请求失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("首次请求成功: %d字节\n", len(response1))
|
||||||
|
|
||||||
|
// 后续请求会复用已有连接
|
||||||
|
response2, err := client.Get(ctx, "https://httpbin.org/uuid")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("第二次请求失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("第二次请求成功: %d字节\n", len(response2))
|
||||||
|
|
||||||
|
// 3. 查看连接池状态
|
||||||
|
manager := &ConnectionPoolManager{
|
||||||
|
client: client,
|
||||||
|
// transport: client.transport,
|
||||||
|
}
|
||||||
|
manager.PrintPoolStats()
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 首次请求成功: [字节数]
|
||||||
|
// 第二次请求成功: [字节数]
|
||||||
|
// === 连接池统计 ===
|
||||||
|
// 当前空闲连接数: 1
|
||||||
|
// 总连接数: 1
|
||||||
|
// 等待队列长度: 0
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user