golang中server.GracefulStop()的行为

时间:2019-04-22 16:44:34

标签: go server grpc goroutine grpc-go

我有一个gRPC服务器,并且已经实现了gRPC服务器的正常关机

fun main() {
    //Some code
    term := make(chan os.Signal)
    go func() {
            if err := grpcServer.Serve(lis); err != nil {
                term <- syscall.SIGINT
            }
        }()

    signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
    <-term
    server.GracefulStop()
    closeDbConnections()
}

这很好。 如果我改为在主goroutine中编写grpcServer.Serve()逻辑,然后将关闭处理程序逻辑放入另一个goroutine中,则通常不会执行server.GracefulStop()之后的语句。如果完全执行closeDbConnections(),则某些DbConnections将关闭。

server.GracefulStop()是阻止呼叫。肯定grpcServer.Serve()server.GracefulStop()完成之前完成。那么,此调用返回后,主goroutine停止需要多长时间?

有问题的代码

func main() {
    term := make(chan os.Signal)
    go func() {
        signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
        <-term
        server.GracefulStop()
        closeDbConnections()
    }()
    if err := grpcServer.Serve(lis); err != nil {
        term <- syscall.SIGINT
    }
}

这种情况无法正常工作。完成server.GracefulStop()之后,closeDbConnections()可能会或可能不会运行(通常不会运行到完成状态)。我正在通过从终端点击Ctrl-C发送SIGINT来测试后一种情况。

有人可以解释这种行为吗?

1 个答案:

答案 0 :(得分:0)

我不确定您的问题(请澄清一下),但是我建议您以这种方式重构main

func main() {

   // ...

   errChan := make(chan error)
   stopChan := make(chan os.Signal)

   // bind OS events to the signal channel
   signal.Notify(stopChan, syscall.SIGTERM, syscall.SIGINT)

   // run blocking call in a separate goroutine, report errors via channel
   go func() {
        if err := grpcServer.Serve(lis); err != nil {
            errChan <- err
        }
    }()

   // terminate your environment gracefully before leaving main function
   defer func() {
      server.GracefulStop()
      closeDbConnections()
   }()

   // block until either OS signal, or server fatal error
   select {
      case err := <-errChan:
          log.Printf("Fatal error: %v\n", err) 
      case <-stopChan:
   }

我不认为像在您的示例中那样混合系统事件和服务器错误是个好主意:万一Serve失败了,您就忽略该错误并发出系统事件,而实际上不会发生当存在两种导致事件终止的不同类型事件的传输(通道)时,请尝试另一种方法。