# 客户端连接池 ## 概述 客户端连接池(Client Pool)是一个用于管理和复用客户端连接的工具,可以显著提高客户端应用的性能和资源利用率。 ## 作用 1. **连接复用**:复用已建立的连接,避免频繁创建和销毁连接,减少系统开销 2. **连接管理**:自动管理连接的生命周期,包括连接创建、验证、清理等 3. **并发控制**:限制最大连接数,防止连接数过多导致服务器压力 4. **资源优化**:预创建最小连接数,提高响应速度;自动清理空闲连接,节省资源 5. **故障恢复**:自动检测和移除无效连接,创建新连接替代 ## 使用方法 ### 1. 创建连接池 ```go import "github.com/noahlann/nnet/pkg/nnet" // 创建连接池配置 poolConfig := &nnet.ClientPoolConfig{ MaxSize: 10, // 最大连接数 MinSize: 2, // 最小连接数(预创建) ClientConfig: &nnet.ClientConfig{ Addr: "tcp://127.0.0.1:8888", ConnectTimeout: 5 * time.Second, ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, }, AcquireTimeout: 5 * time.Second, // 获取连接超时时间 IdleTimeout: 5 * time.Minute, // 空闲连接超时时间 } // 创建连接池 pool, err := nnet.NewClientPool(poolConfig) if err != nil { log.Fatalf("Failed to create connection pool: %v", err) } defer pool.Close() ``` ### 2. 获取和释放连接 ```go // 获取连接 client, err := pool.Acquire() if err != nil { log.Fatalf("Failed to acquire connection: %v", err) } // 使用连接 request := []byte("echo hello\n") resp, err := client.Request(request, 5*time.Second) if err != nil { log.Printf("Request failed: %v", err) } else { fmt.Printf("Response: %s", string(resp)) } // 释放连接(归还到连接池) pool.Release(client) ``` ### 3. 并发使用 ```go var wg sync.WaitGroup for i := 0; i < numRequests; i++ { wg.Add(1) go func(id int) { defer wg.Done() // 从连接池获取连接 client, err := pool.Acquire() if err != nil { log.Printf("Failed to acquire connection: %v", err) return } // 使用完毕后释放连接 defer pool.Release(client) // 使用连接发送请求 request := []byte(fmt.Sprintf("echo request-%d\n", id)) resp, err := client.Request(request, 5*time.Second) if err != nil { log.Printf("Request failed: %v", err) return } fmt.Printf("Response: %s", string(resp)) }(i) } wg.Wait() ``` ### 4. 检查连接池状态 ```go // 获取当前连接数 size := pool.Size() // 获取可用连接数 available := pool.Available() // 检查连接池是否已关闭 isClosed := pool.IsClosed() fmt.Printf("Pool status: Size=%d, Available=%d, IsClosed=%v\n", size, available, isClosed) ``` ## 配置说明 ### ClientPoolConfig - **MaxSize** (int): 最大连接数,默认值为 10 - **MinSize** (int): 最小连接数(预创建),默认值为 2 - **ClientConfig** (*ClientConfig): 客户端配置(地址、超时时间等) - **AcquireTimeout** (time.Duration): 获取连接超时时间,默认值为 10 秒 - **IdleTimeout** (time.Duration): 空闲连接超时时间,默认值为 5 分钟 ### 默认配置 使用 `nnet.DefaultClientPoolConfig()` 获取默认配置: ```go config := nnet.DefaultClientPoolConfig() config.ClientConfig.Addr = "tcp://127.0.0.1:8888" pool, err := nnet.NewClientPool(config) ``` ## 实现细节 ### 1. 连接管理 - 连接池在创建时会预创建 `MinSize` 个连接 - 当连接数小于 `MaxSize` 时,可以动态创建新连接 - 连接在使用完毕后会被归还到连接池,供后续复用 ### 2. 连接验证 - 从连接池获取连接时,会自动检查连接是否有效 - 如果连接无效,会自动创建新连接替代 - 释放连接时,也会检查连接有效性,无效连接会被移除 ### 3. 空闲连接清理 - 如果配置了 `IdleTimeout`,连接池会定期检查并清理无效连接 - 清理间隔为 `IdleTimeout / 2`,确保及时清理无效连接 ### 4. 并发安全 - 连接池使用原子操作和互斥锁保证并发安全 - `Acquire` 和 `Release` 操作是线程安全的 - 多个 goroutine 可以安全地并发使用连接池 ### 5. 连接池关闭 - 调用 `Close()` 方法会关闭所有连接并停止所有 goroutine - 关闭后的连接池不能再次使用 - 已获取的连接在释放时会被关闭(如果连接池已关闭) ## 最佳实践 1. **合理设置连接数**:根据实际并发需求设置 `MaxSize`,避免连接数过多或过少 2. **及时释放连接**:使用 `defer pool.Release(client)` 确保连接被正确释放 3. **处理错误**:始终检查 `Acquire()` 和 `Request()` 的返回错误 4. **资源清理**:在程序退出前调用 `pool.Close()` 关闭连接池 5. **超时设置**:合理设置 `AcquireTimeout` 和 `IdleTimeout`,避免资源浪费 ## 示例 完整示例请参考: - `examples/client_pool/server.go` - 服务器示例 - `examples/client_pool/client.go` - 客户端连接池示例 ## API 参考 ### ClientPool - `Acquire() (Client, error)` - 获取连接 - `Release(client Client) error` - 释放连接 - `Close() error` - 关闭连接池 - `Size() int` - 获取当前连接数 - `Available() int` - 获取可用连接数 - `IsClosed() bool` - 检查连接池是否已关闭 ### ClientPoolConfig - `MaxSize int` - 最大连接数 - `MinSize int` - 最小连接数 - `ClientConfig *ClientConfig` - 客户端配置 - `AcquireTimeout time.Duration` - 获取连接超时时间 - `IdleTimeout time.Duration` - 空闲连接超时时间 ## 实现状态 ✅ **已实现** - 连接池基本功能(获取、释放、关闭) - 连接验证和自动替换 - 并发安全 - 空闲连接清理 - 最大连接数限制 - 最小连接数预创建 - 公共API导出 - 单元测试 - 使用示例 ## 注意事项 1. 连接池目前仅支持 TCP 客户端 2. 连接池不支持连接的健康检查(除了连接有效性检查) 3. 空闲连接清理是基于连接有效性,而不是基于最后使用时间(简化实现) 4. 连接池关闭后,已获取的连接在使用完毕后会被关闭