Introduction

The gproc component provides a unified signal listening and callback processing feature, aimed at resolving the pain points of redundant signal processing logic among multiple different components in a program and the inability to destruct smoothly after receiving exit signals. Without a component for unified exit signal listening, when multiple components use goroutine for asynchronous signal listening, the main goroutine often exits directly upon receiving an exit signal or waits for an unpredictable amount of time to exit, which prevents the program from exiting smoothly and may trigger some unexpected issues. gproc enables each component to receive exit signals effectively and perform corresponding deconstruction processing through unified signal registration and callback processing, ensuring that the program’s signal processing logic is more rigorous.

Relevant methods:

  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()

Brief Introduction:

  • The AddSigHandler method is used for adding listeners and corresponding callback function registrations for specified signals.
  • The AddSigHandlerShutdown method is used for adding listeners and corresponding callback function registrations for common process exit signals, allowing registration of multiple SigHandler.
  • The Listen method is for blocking signal listening and automatically executing callback function calls.

Let’s look at two examples.

Example 1, Using Standard Library Signal Listening

The common code logic for using the standard library signal listening mechanism is as follows:

  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. }

We execute it via the go run command, then exit using the Ctrl+C shortcut (Command+C for Mac users).

  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

As you can see, unfortunately, the MQ goroutine is forcibly closed before it is fully exited.

Example 2, Using gproc Signal Listening

The improved signal listening mechanism using the gproc component is as follows:

  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. }

We execute it via the go run command, then exit using the Ctrl+C shortcut (Command+C for Mac users).

  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

See the difference! All signal listening functions complete normally, and then the process exits smoothly.