Shutdown method
After Go1.8, there is a Shutdown method to be used to elegantly close the (Graceful Shutdown) service.
func (srv *Server) Shutdown(ctx ) error
This method will perform the following operations when called:
1. Close all open listeners
2. Close all idle connections
3. Wait for connections to return to idle state indefinitely
4. Close the service later
Note: Indefinite waiting for 3 can be solved by the context timeout.
RegisterOnShutdown method
After Go1.9, there is a RegisterOnShutdown method, which is used to handle some exit tasks while elegantly shutting down services.
func (srv *Server) RegisterOnShutdown(f func())
Regarding how RegisterOnShutdown takes effect, we look at the code.
type Server struct { ... onShutdown []func() } func (srv *Server) RegisterOnShutdown(f func()) { () = append(, f) () } func (srv *Server) Shutdown(ctx ) error { ... () ... for _, f := range { go f() } () ... }
As we can see from the code, after registering through RegisterOnShutdown, these functions are placed in a function slice, and when Shutdown is called, a goroutine is started for each function for processing.
However, since there is no subsequent processing, there is a small problem here. Shutdown does not wait for these processing functions to end, so it may complete and then end the program before these processing functions.
package main import ( "context" "log" "net/http" "os" "os/signal" "time" ) func main() { var srv (func() { (3 * ) ("Exit func") }) idleConnsClosed := make(chan struct{}) go func() { sigint := make(chan , 1) (sigint, ) <-sigint // We received an interrupt signal, shut down. if err := (()); err != nil { // Error from closing listeners, or context timeout: ("HTTP server Shutdown: %v", err) } close(idleConnsClosed) }() if err := (); err != { // Error starting or closing listener: ("HTTP server ListenAndServe: %v", err) } <-idleConnsClosed }
After Ctrl+c at runtime, the service ends immediately and does not wait for the output of ("Exit func").
Handle exit function
To solve this problem we use to handle these exit functions, so we have the following code (decoupled and added some logs):
package main import ( "context" "log" "net/http" "os" "os/signal" "sync" "time" ) func main() { var srv var wg register := func(f func()) { (1) ("Exit func register") (func() { defer () f() ("Exit func done") }) } register(func() { (3 * ) ("Called on Shutdown") }) idleConnsClosed := make(chan struct{}) go func() { sigint := make(chan , 1) (sigint, ) <-sigint // We received an interrupt signal, shut down. ("Shutdown start") if err := (()); err != nil { // Error from closing listeners, or context timeout: ("HTTP server Shutdown: %v", err) } close(idleConnsClosed) }() if err := (); err != { // Error starting or closing listener: ("HTTP server ListenAndServe: %v", err) } <-idleConnsClosed ("Shutdown finish") () }
We used (3 * ) to simulate a long exit program when RegisterOnShutdown, and waited after "Shutdown finish" to wait for the exit program to complete the task.
The execution log is as follows:
2023/05/12 16:48:53 Exit func register
^C2023/05/12 16:48:54 Shutdown start
2023/05/12 16:48:54 Shutdown finish
2023/05/12 16:48:57 Called on Shutdown
2023/05/12 16:48:57 Exit func done
You can see that the service itself is very light. After Shutdown start, it immediately finishes and comes to(), and waits for the register function to complete before exiting the program.
In addition, if you need to start some other tasks during the service operation, you can also use
(1) go func() { defer () ... }
To ensure that the task can be completed smoothly after serving Shutdown.
The above is the detailed content of how go to elegantly close the Graceful Shutdown service. For more information about go to close Graceful Shutdown, please follow my other related articles!