版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://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
}

如果我的文章对你有帮助,你可以给我打赏以促使我拿出更多的时间和精力来分享我的经验和思考总结。

微信支付码

anyShare分享到:

发表评论

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.