You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.2 KiB

客户端连接池

概述

客户端连接池Client Pool是一个用于管理和复用客户端连接的工具可以显著提高客户端应用的性能和资源利用率。

作用

  1. 连接复用:复用已建立的连接,避免频繁创建和销毁连接,减少系统开销
  2. 连接管理:自动管理连接的生命周期,包括连接创建、验证、清理等
  3. 并发控制:限制最大连接数,防止连接数过多导致服务器压力
  4. 资源优化:预创建最小连接数,提高响应速度;自动清理空闲连接,节省资源
  5. 故障恢复:自动检测和移除无效连接,创建新连接替代

使用方法

1. 创建连接池

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. 获取和释放连接

// 获取连接
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. 并发使用

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. 检查连接池状态

// 获取当前连接数
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() 获取默认配置:

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. 并发安全

  • 连接池使用原子操作和互斥锁保证并发安全
  • AcquireRelease 操作是线程安全的
  • 多个 goroutine 可以安全地并发使用连接池

5. 连接池关闭

  • 调用 Close() 方法会关闭所有连接并停止所有 goroutine
  • 关闭后的连接池不能再次使用
  • 已获取的连接在释放时会被关闭(如果连接池已关闭)

最佳实践

  1. 合理设置连接数:根据实际并发需求设置 MaxSize,避免连接数过多或过少
  2. 及时释放连接:使用 defer pool.Release(client) 确保连接被正确释放
  3. 处理错误:始终检查 Acquire()Request() 的返回错误
  4. 资源清理:在程序退出前调用 pool.Close() 关闭连接池
  5. 超时设置:合理设置 AcquireTimeoutIdleTimeout,避免资源浪费

示例

完整示例请参考:

  • 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. 连接池关闭后,已获取的连接在使用完毕后会被关闭