Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc

1. 引言

线上的某个服务(Golang开发)使用Redis作为消息队列,使用的redis库是garyburd/redigo, 这两天出现如下错误

connection pool exhausted

2. 产生原因

阅读源码pool.go 阅读get()即可

type Pool struct {

    // Dial()方法返回一个连接,从在需要创建连接到的时候调用
    Dial func() (Conn, error)

    // TestOnBorrow()方法是一个可选项,该方法用来诊断一个连接的健康状态
    TestOnBorrow func(c Conn, t time.Time) error

    // 最大空闲连接数
    MaxIdle int

    // 一个pool所能分配的最大的连接数目
    // 当设置成0的时候,该pool连接数没有限制
    MaxActive int

    // 空闲连接超时时间,超过超时时间的空闲连接会被关闭。
    // 如果设置成0,空闲连接将不会被关闭
    // 应该设置一个比redis服务端超时时间更短的时间
    IdleTimeout time.Duration

    // 如果Wait被设置成true,则Get()方法将会阻塞
    Wait bool
    ... ...
}

以上异常的原因是这样的,在garyburd/redigo中,得到连接的步骤如下
1. 尝试从空闲列表中,获得一个可用连接;如果成功直接返回,失败则尝试步骤2
2. 如果当前的Active 连接数 < MaxActive,则尝试创建一个新连接;如果成功直接返回,失败则尝试步骤3
3. 判断Wait为true则等待,否则报出异常

// ErrPoolExhausted is returned from a pool connection method (Do, Send,
// Receive, Flush, Err) when the maximum number of database connections in the
// pool has been reached.
var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")

3. 解决方法

  • 1) 设置MaxActive,设MaxActive=0(表示无限大)或者足够大。
  • 2) 设置Wait=true,当程序执行get(),无法获得可用连接时,将会暂时阻塞。

4. 完整的初始化连接池的代码

func NewRedisPool(redisConf context.RedisConf) *redis.Pool {

    address := fmt.Sprintf("%v:%v", redisConf.Host, redisConf.Port)
    dbOption := redis.DialDatabase(redisConf.Db)
    pwOption := redis.DialPassword(redisConf.Password)
    // **重要** 设置读写超时
    readTimeout := redis.DialReadTimeout(time.Second * time.Duration(redisConf.ConTimeout))
    writeTimeout := redis.DialWriteTimeout(time.Second * time.Duration(redisConf.ConTimeout))
    conTimeout := redis.DialConnectTimeout(time.Second * time.Duration(redisConf.ConTimeout))

    redisPool := &redis.Pool{
        // 从配置文件获取maxidle以及maxactive,取不到则用后面的默认值
        MaxIdle:     redisConf.MaxIdleConn,
        MaxActive:   redisConf.MaxActiveConn,
        // **重要** 如果空闲列表中没有可用的连接
        // 且当前Active连接数 < MaxActive
        // 则等待
        Wait:        true,
        IdleTimeout: time.Duration(redisConf.IdleTimeout) * time.Second,
        Dial: func() (redis.Conn, error) {
            c, err := redis.Dial("tcp", address, dbOption, pwOption,
                readTimeout, writeTimeout, conTimeout)
            if err != nil {
                return nil, err
            }
            return c, nil
        },
    }
    return redisPool
}

微信公众号

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据