【问题标题】:Behavior of server.GracefulStop() in golanggolang 中 server.GracefulStop() 的行为
【发布时间】:2019-09-11 20:25:19
【问题描述】:

我有一个 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() 完成之前完成。那么,这个调用返回后,main 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 来测试后一种情况。

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

【问题讨论】:

  • 您没有提供问题代码的示例,但如果您没有等待您的 goroutine 在 main 中返回,您将在程序完成之前退出程序。
  • @JimB goroutine 只是在运行服务器,main 直到GracefulStop 完成才返回,所以我看不出有问题。 @agyeya,回答你的问题,不需要时间。当main 返回时(在函数结束时),进程退出。
  • @Adrian:他们说“这很好用”,这是我们似乎没有的非工作案例。这听起来像是基本的“goroutines 之前的主要退出”问题。
  • 哦,我明白你在说什么 - 是的,这将是假设(但未显示)代码中的问题。
  • 添加了有问题的代码。只是想了解它的问题,我的用例对其他代码感到满意

标签: go server grpc goroutine grpc-go


【解决方案1】:

我不确定您的问题(请澄清),但我建议您以这种方式重构您的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 失败,您只需忽略错误并发出系统事件,实际上并没有发生。当有两种传输(通道)用于导致进程终止的两种不同类型的事件时,请尝试另一种方法。

【讨论】:

  • 您好,我添加了有问题的代码。如果我对 goroutines 执行的理解似乎存在差距,请告诉我。
  • @agyeya 我建议您研究我上面提供的代码示例并将其与您的进行比较。这就是我们在后端的做法。这种方法经过充分测试且可靠。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2014-09-03
  • 2021-08-01
  • 1970-01-01
  • 2013-06-27
  • 2017-10-16
  • 2018-11-23
相关资源
最近更新 更多