|
|
# 客户端连接池
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
客户端连接池(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. 连接池关闭后,已获取的连接在使用完毕后会被关闭
|
|
|
|