让我们先从pool的数据结构说起:
Dial:是必须要实现的,就是调用普通的的redis.Dial即可。
TestOnBorrow:在获取conn的时候会调用一次这个方法,来保证连接可用(其实也不是一定可用,因为test成功以后依然有可能被干掉),这个方法是可选项,一般这个方法是去调用一个redis的ping方法,看项目需求了,如果并发很高,想极限提高速度,这个可以不设置。如果想增加点连接可用性,还是加上比较好。看个人取舍了。
MaxIdle,idle,IdleTimeout:都是idle相关设置,MaxIdle决定idle的列表长度,IdleTimeout决定了idle列表中conn的生命周期,默认为0,即lifetime,如果设置了idleTimeout的话,在获取conn的时候会检查idle队列中的连接,把超时的连接关闭掉。
MaxActive:线程池的最大连接数。线程池都是优先复用已有的连接,但是不可避免总是会有新建连接的时候,那么这个值就是限定了连接数的上限。
Wait:当连接数已满,是否要阻塞等待获取连接。false表示不等待,直接返回错误。
下面我们分析一下核心的获取连接的方法
// get prunes stale connections and returns a connection from the idle list or
// creates a new connection.
func (p *Pool) get() (Conn, error) {
p.mu.Lock()
// Prune stale connections.
//检查超时的连接,如果超时,则从idle队列中移除
if timeout := p.IdleTimeout; timeout > 0 {
for i, n := 0, p.idle.Len(); i < n; i++ {
e := p.idle.Back()
if e == nil {
break
}
ic := e.Value.(idleConn)
if ic.t.Add(timeout).After(nowFunc()) {
break
}
p.idle.Remove(e)
p.release()
p.mu.Unlock()
ic.c.Close()
p.mu.Lock()
}
}
for {
// Get idle connection.
//从idle队列中复用已有的连接
for i, n := 0, p.idle.Len(); i < n; i++ {
e := p.idle.Front()
if e == nil {
break
}
ic := e.Value.(idleConn)
p.idle.Remove(e)
test := p.TestOnBorrow
p.mu.Unlock()
if test == nil || test(ic.c, ic.t) == nil {
return ic.c, nil
}
ic.c.Close()
p.mu.Lock()
p.release()
}
// Check for pool closed before dialing a new connection.
if p.closed {
p.mu.Unlock()
return nil, errors.New("redigo: get on closed pool")
}
// Dial new connection if under limit.
//还没达到最大连接数,idle中又没有可用的连接,那么就new一个连接出来
if p.MaxActive == 0 || p.active < p.MaxActive {
dial := p.Dial
p.active += 1
p.mu.Unlock()
c, err := dial()
if err != nil {
p.mu.Lock()
p.release()
p.mu.Unlock()
c = nil
}
return c, err
}
if !p.Wait {
p.mu.Unlock()
return nil, ErrPoolExhausted
}
if p.cond == nil {
p.cond = sync.NewCond(&p.mu)
}
p.cond.Wait()
}
}
综上可知,想提高redis的处理速度,可以把idle设置的大一点,当然active的值是一定要比idle大的(0表示不限制)。这里还有一点需要注意的redis本身支持的连接数设置问题,刚才分析的都是client端的情况,如果server只支持100的连接数,那客户端的pool设定再多也没有用,redis的连接数配置又是另外一个话题,这里就不展开讲。
网友评论