1.连接池的失败重试机制
go从连接池中获取连接时有两个策略:
alwaysNewConn
cachedOrNewConn
在获取连接时go有个失败重试机制,在以下方法中:
func (db *DB) PingContext(ctx context.Context) error {
var dc *driverConn
var err error
// maxBadConnRetries是个静态变量为2,这里最多会执行两次从连接池中获取连接,如果在两次获取
// 过程中获取到可用连接则直接返回
for i := 0; i < maxBadConnRetries; i++ {
dc, err = db.conn(ctx, cachedOrNewConn)
// var ErrBadConn = errors.New("driver: bad connection")
// 标记是否是一个bad连接
if err != driver.ErrBadConn {
break
}
}
// 如果两次都获取不到可用连接,则以请求获取一个新连接的方式获取并返回
if err == driver.ErrBadConn {
dc, err = db.conn(ctx, alwaysNewConn)
}
if err != nil {
return err
}
return db.pingDC(ctx, dc, dc.releaseConn)
}
ErrBadConn
2.连接池如何清理bad连接
ErrBadConnErrBadConnresetterChresetterChErrBadConn
func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
.....
select {
default:
// 这里就是对dc暂时标记为ErrBadConn
dc.lastErr = driver.ErrBadConn
dc.Unlock()
case db.resetterCh <- dc:
}
}
3.连接池清理了bad连接后numOpen又是如何保证准确的
ErrBadConnClose()removeDepLocked()
func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error {
// db.dep会保存DB中的依赖
xdep, ok := db.dep[x]
if !ok {
panic(fmt.Sprintf("unpaired removeDep: no deps for %T", x))
}
l0 := len(xdep)
delete(xdep, dep)
switch len(xdep) {
case l0:
// 没删除成功直接panic
panic(fmt.Sprintf("unpaired removeDep: no %T dep on %T", dep, x))
case 0:
// 依赖已经全部被删除
delete(db.dep, x)
return x.finalClose
default:
// 不为0,依赖还存在,返回nil,不执行finalClose,连接继续保留
return func() error { return nil }
}
}
再看下finalClose接口方法:
func (dc *driverConn) finalClose() error {
......
dc.db.mu.Lock()
// 这里会执行numOpen--,保证numOpen能准确统计当前连接数
dc.db.numOpen--
dc.db.maybeOpenNewConnections()
dc.db.mu.Unlock()
atomic.AddUint64(&dc.db.numClosed, 1)
return err
}