基本介绍

gproc 组件提供了统一的信号监听和回调处理功能,目的是解决在程序中多个不同组件冗余的信号处理逻辑,以及接收退出信号后无法平滑析构的痛点。在没有统一退出信号监听的组件下,当多个组件通过 goroutine 异步监听信号,主 goroutine 接收到退出信号往往会直接退出,或者等待一段无法预测的时间退出,造成程序其实无法平滑退出,可能引起一些不可预料的问题。 gproc 通过统一的信号注册和回调处理,使得各个组件能够有效地接收到退出信号并做相应析构处理,使得程序的信号处理逻辑更加严谨。

相关方法:

  1. // AddSigHandler adds custom signal handler for custom one or more signals.
  2. func AddSigHandler(handler SigHandler, signals ...os.Signal)
  3. // AddSigHandlerShutdown adds custom signal handler for shutdown signals:
  4. // syscall.SIGINT,
  5. // syscall.SIGQUIT,
  6. // syscall.SIGKILL,
  7. // syscall.SIGTERM,
  8. // syscall.SIGABRT.
  9. func AddSigHandlerShutdown(handler ...SigHandler)
  10. // Listen blocks and does signal listening and handling.
  11. func Listen()

简要介绍:

  • AddSigHanlder 方法用于添加对指定信号的监听和对应回调函数注册。
  • AddSigHandlerShutdown 方法用于添加对常见进程退出信号的监听和对应回调函数注册,可以注册多个 SigHandler
  • Listen 方法用于阻塞监听信号并自动执行回调函数调用。

我们来看两个示例。

示例1,使用标准库信号监听

使用标准库信号监听机制的常见代码逻辑如下:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "os/signal"
  6. "syscall"
  7. "time"
  8. )
  9. func signalHandlerForMQ() {
  10. var (
  11. sig os.Signal
  12. receivedChan = make(chan os.Signal)
  13. )
  14. signal.Notify(
  15. receivedChan,
  16. syscall.SIGINT,
  17. syscall.SIGQUIT,
  18. syscall.SIGKILL,
  19. syscall.SIGTERM,
  20. syscall.SIGABRT,
  21. )
  22. for {
  23. sig = <-receivedChan
  24. fmt.Println("MQ is shutting down due to signal:", sig.String())
  25. time.Sleep(time.Second)
  26. fmt.Println("MQ is shut down smoothly")
  27. return
  28. }
  29. }
  30. func main() {
  31. fmt.Println("Process start, pid:", os.Getpid())
  32. go signalHandlerForMQ()
  33. var (
  34. sig os.Signal
  35. receivedChan = make(chan os.Signal)
  36. )
  37. signal.Notify(
  38. receivedChan,
  39. syscall.SIGINT,
  40. syscall.SIGQUIT,
  41. syscall.SIGKILL,
  42. syscall.SIGTERM,
  43. syscall.SIGABRT,
  44. )
  45. for {
  46. sig = <-receivedChan
  47. fmt.Println("MainProcess is shutting down due to signal:", sig.String())
  48. return
  49. }
  50. }

我们通过 go run 命令来执行一下,随后通过 Ctrl+C 快捷键退出( Mac 用户通过 Command+C)。

  1. $ go run signal_handler.go
  2. Process start, pid: 21928
  3. ^CMainProcess is shutting down due to signal: interrupt
  4. MQ is shutting down due to signal: interrupt

可以看到,好可惜,那个 MQgoroutine 还没完全退出进程即被强行关闭。

示例2,使用 gproc 信号监听

使用 gproc 组件改进后的信号监听机制如下:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/os/gproc"
  5. "os"
  6. "time"
  7. )
  8. func signalHandlerForMQ(sig os.Signal) {
  9. fmt.Println("MQ is shutting down due to signal:", sig.String())
  10. time.Sleep(time.Second)
  11. fmt.Println("MQ is shut down smoothly")
  12. }
  13. func signalHandlerForMain(sig os.Signal) {
  14. fmt.Println("MainProcess is shutting down due to signal:", sig.String())
  15. }
  16. func main() {
  17. fmt.Println("Process start, pid:", os.Getpid())
  18. gproc.AddSigHandlerShutdown(
  19. signalHandlerForMQ,
  20. signalHandlerForMain,
  21. )
  22. gproc.Listen()
  23. }

我们通过 go run 命令来执行一下,随后通过 Ctrl+C 快捷键退出( Mac 用户通过 Command+C)。

  1. $ go run signal_handler_gproc.go
  2. Process start, pid: 22876
  3. ^CMQ is shutting down due to signal: interrupt
  4. MainProcess is shutting down due to signal: interrupt
  5. MQ is shut down smoothly

看到差别了吧!所有的信号监听函数都正常结束后,随后进程平滑退出。