版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | 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(&quot;redigo: connection pool exhausted&quot;)

3. 解决方法

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

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

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

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

微信公众号