HTTP with Go: Graceful Shutdown
Implementing graceful HTTP shutdown with Golang.
Last updated on: 2024-12-12
Graceful shutdown implementation for a Go HTTP server:
func run() (exitcode int) {
// Listen for interrupt signal (and notify context when received).
mainCtx, mainCtxCancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer mainCtxCancel()
// Initialize HTTP server.
httpServer := &http.Server{ /* TODO */ }
httpServer.BaseContext = func(_ net.Listener) context.Context { return mainCtx }
// Run HTTP server in seperate Go routine.
httpErrc := make(chan error, 1)
go func() {
err := httpServer.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
httpErrc <- err
}
}()
// Wait for interrupt signal or server error.
select {
case err := <-httpErrc:
logger.Error("HTTP server exited", "error", err)
case <-mainCtx.Done():
logger.Debug("received exit signal", "cause", mainCtx.Err())
}
exitCtx, exitCtxCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer exitCtxCancel()
// Shutdown HTTP server.
err := httpServer.Shutdown(exitCtx)
if err != nil { logger.Error("HTTP shutdown failed", "error", err) }
// Do other cleanup tasks (ex: closing database connection).
// ...
}
Using the previous code, you can be notified of interrupts in your HTTP handlers via the request's context (`http.Request.Context()`).
For example, this handler will block until an interrupt:
func handle(w http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
}
NB: As part of the default `http.Server` behavior, if the HTTP client closes the underyling network connection, the request's context is also done.