listen for incoming requests
Gracefully shutting down an HTTP server in Go is important to ensure no requests are abruptly dropped when stopping the server. By implementing a proper shutdown handler, we can allow pending requests to complete while rejecting any new requests.
In this guide, we’ll walk through how to code a graceful shutdown in Go using the net/http package.
Overview
To enable graceful shutdowns. we need to
- Create a shutdown channel to receive shutdown signals
- Pass this channel to our http.Server
- Define a shutdown handler function to initiate the shutdown
- Call http.Server’s Shutdown() method to start the shutdown process
By wiring up these pieces, our Go server will cleanly finish ongoing requests before shutting down when we trigger it.
Implementing the Shutdown Logic
1. Create a Channel
We’ll use a channel to synchronize the shutdown signal:
quit := make(chan os.Signal)
This creates a quit channel to receive values. We’ll use this to listen for interrupt signals.
2. Pass Channel to Server
When creating the server, we pass the quit channel:
srv := &http.Server{Addr: ":8080", Handler: router, BaseContext: func(net.Listener) context.Context { return context.WithValue(context.Background(), "chan", quit)}}
This makes the quit channel available to request contexts.
3. Define the Shutdown Handler
Next, we’ll define a handler to initiate the graceful shutdown:
func gracefulShutdown(srv *http.Server) { // Start http shutdown log.Println("Shutting down server...") err := srv.Shutdown(context.Background()) if err != nil { log.Fatal(err) } // Wait for interrupt signals <-quit // Disconnect from the database db.Close() log.Println("Server gracefully stopped")}
This begins the shutdown process on the server. It waits for interrupt signals on the quit channel, then closes the database connection pool before exiting.
4. Trigger Shutdown
Finally, we trigger the shutdown by sending a value on the quit channel:
quit <- os.Interrupt
When our main function receives an interrupt, it will execute this to start the graceful shutdown process.
5. Putting It All Together
Our main function now looks like:
func main() { // Create quit channel quit := make(chan os.Signal) // Create server srv := &http.Server{Addr: ":8080", Handler: router, BaseContext: func(net.Listener) context.Context { return context.WithValue(context.Background(), "chan", quit) }} // Start server go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal(err) } }() // Wait for close <-quit // Graceful shutdown gracefulShutdown(srv)}
When an interrupt signal is received on quit, it will call gracefulShutdown() to cleanly stop the server.
Customizing Shutdown Behavior
There are a few ways we can customize the shutdown behavior:
Set a Timeout
Use the Shutdown() context to configure a max shutdown time:
err := srv.Shutdown(context.WithTimeout(context.Background(), 10*time.Second))
This will forcefully terminate remaining connections after 10 seconds.
Reject New Connections
Call the Close() method before Shutdown() to reject new connections:
srv.Close()err := srv.Shutdown(context.Background())
Custom Handler
Define a custom handler to control the shutdown sequence:
srv.RegisterOnShutdown(func() { // Custom logic})
This lets you hook into the shutdown event.
Implementing graceful shutdown ensures your Go web server terminates cleanly and reliably. By leveraging net/http primitives like the Shutdown() method and context values, we can build a robust shutdown process that avoids dropping connections.
Key takeaways include:
- Use a channel to synchronize shutdown signals
- Pass the channel in server contexts
- Trigger shutdown via the channel
- Handle custom shutdown logic with handlers
With these best practices, you can cleanly stop your Go HTTP servers and prevent availability issues caused by abrupt shutdowns.
you can use reflect.TypeOf(x) to inspect the type of a variable x. This will return a reflect.Type object. Similarly
import it using import “”reflect””. You can then inspect the type and value of a variable using the reflect.TypeOf() and reflect.ValueOf() functions
respectively.
For example
you can use reflect.ValueOf(x) to inspect the value of x. This will return a reflect.Value object.
The methods provided by these objects can be used to further inspect and manipulate the variables type and value.
How do you use the “”testing”” package to write unit tests in Go?
To write unit tests in Go using the “”testing”” package