redis连接池运转流程
本文主要介绍redis-pool的PoolSize与MinIdleConns的参数配置对pool中连接数conns以及空闲连接数idleConns的影响
PoolSize默认为 10 * runtime.NumCPU()
连接池对象:
p := &ConnPool{
conns: make([]*Conn, 0, opt.PoolSize),
idleConns: make([]*Conn, 0, opt.PoolSize),
}
在一开始NewPool时,判断MinIdleConns配置,而后初始化连接
for i := 0; i < opt.MinIdleConns; i++ {
p.checkMinIdleConns()
}
func (p *ConnPool) checkMinIdleConns() {
if p.opt.MinIdleConns == 0 {
return
}
if p.poolSize < p.opt.PoolSize && p.idleConnsLen < p.opt.MinIdleConns {
p.poolSize++
p.idleConnsLen++
go p.addIdleConn()
}
}
func (p *ConnPool) addIdleConn() {
cn, err := p.newConn(true)
if err != nil {
return
}
p.connsMu.Lock()
p.conns = append(p.conns, cn)
p.idleConns = append(p.idleConns, cn)
p.connsMu.Unlock()
}
我们会发现,建立的连接,最大为min(PoolSize,MinIdleConns),如PoolSize=10,MinIdleConns为8,则最终为8个Conns和IdleConns,并且conn的pooled字段为true
当我们执行一个redis指令时,会从IdleConns中获取,取最后一个,而后剔除切片,如果当前idleConnsLen少了,则又会checkMinIdleConns->addIdleConn,补全空闲连接。
func (p *ConnPool) Get() (*Conn, error) {
for {
p.connsMu.Lock()
cn := p.popIdle()
p.connsMu.Unlock()
if cn == nil {
break
}
return cn, nil
}
newcn, err := p._NewConn(true)
if err != nil {
return nil, err
}
return newcn, nil
}
从空闲连接中获取连接
func (p *ConnPool) popIdle() *Conn {
if len(p.idleConns) == 0 {
return nil
}
idx := len(p.idleConns) - 1
cn := p.idleConns[idx]
p.idleConns = p.idleConns[:idx]
p.idleConnsLen--
p.checkMinIdleConns()
return cn
}
当空闲连接中获取不到时,新建连接,pooled参数为true,如果当前连接池的poolSize超过配置数时,cn.pooled = false
func (p *ConnPool) _NewConn(pooled bool) (*Conn, error) {
cn, err := p.newConn(pooled)
if err != nil {
return nil, err
}
p.connsMu.Lock()
p.conns = append(p.conns, cn)
if pooled {
if p.poolSize < p.opt.PoolSize {
p.poolSize++
} else {
cn.pooled = false
}
}
p.connsMu.Unlock()
return cn, nil
}
当redis指令执行完时,判断err,执行Put或者Remove方法
if err == nil || internal.IsRedisError(err) {
c.connPool.Put(cn)
} else {
c.connPool.Remove(cn, err)
}
如果当前连接池的poolSize超过配置数时,cn.pooled = false时,直接Remove当前连接,从当前的conns中剔除,并且close连接,当cn.pooled为true时,直接塞入p.idleConns列表,连接得到复用
func (p *ConnPool) Put(cn *Conn) {
if !cn.pooled {
p.Remove(cn, nil)
return
}
p.connsMu.Lock()
p.idleConns = append(p.idleConns, cn)
p.idleConnsLen++
p.connsMu.Unlock()
p.freeTurn()
}
当前,最大连接数Conns也收参数poolsize的影响,最多创建连接不会超过此配置,通过一个channel管道实现
queue: make(chan struct{}, opt.PoolSize),
至此,连接的整个生命周期完成,由此看到,空闲连接数的配置,会严重影响连接的复用,当空闲连接配置过小时,而并发量又很大时,会频繁的close Tcp连接