kill -9
RSTconnection refusedkill -9

很间接的感触就是:在重启过程中,会有一段时间不能给用户提供失常服务;同时粗鲁敞开服务,也可能会对业务依赖的数据库等状态服务造成净化。

所以咱们服务重启或者是从新公布过程中,要做到新旧服务无缝切换,同时能够保障变更服务 零宕机工夫

go-zero

优雅退出

在实现优雅重启之前首先须要解决的一个问题是 如何优雅退出

fdlisten
httpserver.ShutDown()
inShutdownlisteners

别离来解释一下这几个步骤的含意:

inShutdown

func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
        return ErrServerClosed
    }
    ....
    // 理论监听端口;生成一个 listener
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    // 进行理论逻辑解决,并将该 listener 注入
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

func (s *Server) shuttingDown() bool {
    return atomic.LoadInt32(&s.inShutdown) != 0
}
ListenAndServeServer
inShutdown

listeners

func (srv *Server) Serve(l net.Listener) error {
    ...
    // 将注入的 listener 退出外部的 map 中
    // 不便后续管制从该 listener 链接到的申请
    if !srv.trackListener(&l, true) {
        return ErrServerClosed
    }
    defer srv.trackListener(&l, false)
    ...
}
Servelisteners maplistenerShutDownlistenerslistener.Close()

closeIdleConns

Server

敞开

func (srv *Server) Serve(l net.Listener) error {
  ...
  for {
    rw, err := l.Accept()
    // 此时 accept 会产生谬误,因为后面曾经将 listener close了
    if err != nil {
      select {
      // 又是一个标记:doneChan
      case <-srv.getDoneChan():
        return ErrServerClosed
      default:
      }
    }
  }
}
getDoneChanlistenerdoneChan
Shutdown

但服务启动后的某一时刻,程序如何晓得服务被中断了呢?服务被中断时如何告诉程序,而后调用Shutdown作解决呢?接下来看一下零碎信号告诉函数的作用

服务中断

signalsignalNotify

https://github.com/tal-tech/go-zero/blob/master/core/proc/signals.go

func init() {
  go func() {
    var profiler Stopper
    
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGTERM)

    for {
      v := <-signals
      switch v {
      case syscall.SIGUSR1:
        dumpGoroutines()
      case syscall.SIGUSR2:
        if profiler == nil {
          profiler = StartProfile()
        } else {
          profiler.Stop()
          profiler = nil
        }
      case syscall.SIGTERM:
        // 正在执行优雅敞开的中央
        gracefulStop(signals)
      default:
        logx.Error("Got unregistered signal:", v)
      }
    }
  }()
}
SIGUSR1goroutineSIGUSR2SIGTERMgracefulStop
gracefulStop
wrap uptime.Sleep()shutdown

这样,服务不再承受新的申请,服务沉闷的申请期待解决实现,同时也期待资源敞开(数据库连贯等),如有超时,强制退出。

整体流程

dockerk8sSIGTERMShutDown

到这里,整个优雅敞开的流程就梳理结束了。

k8s
old podnew podold podnew podold pod
new podold pod

我的项目地址

https://github.com/tal-tech/go-zero

欢送应用 go-zero 并 star 反对咱们!

微信交换群

关注『微服务实际』公众号并点击 交换群 获取社区群二维码。