// Close closes the server.func(s*Server)Close(){s.startShutdown()s.rwlock.Lock()// prevent new connectionsdefers.rwlock.Unlock()ifs.listener!=nil{err:=s.listener.Close()terror.Log(errors.Trace(err))s.listener=nil}ifs.socket!=nil{err:=s.socket.Close()terror.Log(errors.Trace(err))s.socket=nil}ifs.statusServer!=nil{err:=s.statusServer.Close()terror.Log(errors.Trace(err))s.statusServer=nil}ifs.grpcServer!=nil{s.grpcServer.Stop()s.grpcServer=nil}ifs.autoIDService!=nil{s.autoIDService.Close()}ifs.authTokenCancelFunc!=nil{s.authTokenCancelFunc()}s.wg.Wait()metrics.ServerEventCounter.WithLabelValues(metrics.EventClose).Inc()}func(s*Server)startShutdown(){s.rwlock.RLock()logutil.BgLogger().Info("setting tidb-server to report unhealthy (shutting-down)")s.inShutdownMode=trues.rwlock.RUnlock()// give the load balancer a chance to receive a few unhealthy health reports// before acquiring the s.rwlock and blocking connections.waitTime:=time.Duration(s.cfg.GracefulWaitBeforeShutdown)*time.SecondifwaitTime>0{logutil.BgLogger().Info("waiting for stray connections before starting shutdown process",zap.Duration("waitTime",waitTime))time.Sleep(waitTime)}}
vargracefulCloseConnectionsTimeout=15*time.Second// TryGracefulDown will try to gracefully close all connection first with timeout. if timeout, will close all connection directly.func(s*Server)TryGracefulDown(){ctx,cancel:=context.WithTimeout(context.Background(),gracefulCloseConnectionsTimeout)defercancel()done:=make(chanstruct{})gofunc(){s.GracefulDown(ctx,done)}()select{case<-ctx.Done():s.KillAllConnections()case<-done:return}}
// GracefulDown waits all clients to close.func(s*Server)GracefulDown(ctxcontext.Context,donechanstruct{}){logutil.Logger(ctx).Info("[server] graceful shutdown.")metrics.ServerEventCounter.WithLabelValues(metrics.EventGracefulDown).Inc()count:=s.ConnectionCount()fori:=0;count>0;i++{s.kickIdleConnection()count=s.ConnectionCount()ifcount==0{break}// Print information for every 30s.ifi%30==0{logutil.Logger(ctx).Info("graceful shutdown...",zap.Int("conn count",count))}ticker:=time.After(time.Second)select{case<-ctx.Done():returncase<-ticker:}}close(done)}
ConnectionCount
判断连接个数的逻辑也很简单,就是对算下 s.clients 的 length
// ConnectionCount gets current connection count.func(s*Server)ConnectionCount()int{s.rwlock.RLock()cnt:=len(s.clients)s.rwlock.RUnlock()returncnt}
其中还有一个奇怪的函数 kickIdleConnection,这个是做什么的?
kickIdleConnection
看逻辑是收集可以被close的会话然后close掉。
func(s*Server)kickIdleConnection(){varconns[]*clientConns.rwlock.RLock()for_,cc:=ranges.clients{ifcc.ShutdownOrNotify(){// Shutdowned conn will be closed by us, and notified conn will exist themselves.conns=append(conns,cc)}}s.rwlock.RUnlock()for_,cc:=rangeconns{err:=cc.Close()iferr!=nil{logutil.BgLogger().Error("close connection",zap.Error(err))}}}
// ShutdownOrNotify will Shutdown this client connection, or do its best to notify.func(cc*clientConn)ShutdownOrNotify()bool{if(cc.ctx.Status()&mysql.ServerStatusInTrans)>0{returnfalse}// If the client connection status is reading, it's safe to shutdown it.ifatomic.CompareAndSwapInt32(&cc.status,connStatusReading,connStatusShutdown){returntrue}// If the client connection status is dispatching, we can't shutdown it immediately,// so set the status to WaitShutdown as a notification, the loop in clientConn.Run// will detect it and then exit.atomic.StoreInt32(&cc.status,connStatusWaitShutdown)returnfalse}const(connStatusDispatchingint32=iotaconnStatusReadingconnStatusShutdown// Closed by server.connStatusWaitShutdown// Notified by server to close.)