Building a Graceful Shutdown in Go with net/http

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:

go

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:

go

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:

go

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:

go

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:

go

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:

go

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:

go

srv.Close()err := srv.Shutdown(context.Background())

Custom Handler

Define a custom handler to control the shutdown sequence:

go

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

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *