I am trying to find out if there is any difference between two implementations that currently do same thing - gracefully shutdown application/server, for example when Ctrl+C
is hit. Both works fine and are based on the documentation.
What a friend of mine says is that, the example 2 handles shutdown at application level which shuts down all contexts throughout the application. However, the example 1 does it in HTTP server level which doesn’t necessarily shut down all contexts throughout the application. Since I am a beginner I cannot argue back and need your input on this please.
Example 1
The signals are handled in http.go file so the whole graceful shutdown has been handled in a single file.
cmd/app/main.go
package main
import (
"context"
"internal/http"
"os"
"os/signal"
"syscall"
)
func main() {
// Bootstrap config, logger, etc
http.Start()
}
internal/http/server.go
package http
import (
"context"
"github.com/prometheus/common/log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func Start() {
log.Infof("starting HTTP server")
srv := &http.Server{Addr: ":8080", Handler: nil}
idle := make(chan struct{})
go shutdown(srv, idle)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("failed to start/close HTTP server [%v]", err)
}
<-idle
log.Info("gracefully shutdown HTTP server")
}
func shutdown(srv *http.Server, idle chan<- struct{}) {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-sig
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("shutdown HTTP server by interrupting idle connections [%v]", err)
}
close(idle)
}
Example 2
The signals are handled in application’s main.go file so the whole graceful shutdown has been split within two files. The only addition is this example uses WithCancel
context.
cmd/app/main.go
package main
import (
"context"
"internal/http"
"os"
"os/signal"
"syscall"
)
func main() {
// Bootstrap config, logger, etc
backgroundCtx := context.Background()
withCancelCtx, cancel := context.WithCancel(backgroundCtx)
go shutdown(cancel)
http.Start(withCancelCtx)
}
func shutdown(cancel context.CancelFunc) {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-sig
cancel()
}
internal/http/server.go
package http
import (
"context"
"github.com/prometheus/common/log"
"net/http"
"time"
)
func Start(ctx context.Context) {
log.Infof("starting HTTP server")
srv := &http.Server{Addr: ":8080", Handler: nil}
idle := make(chan struct{})
go shutdown(ctx, srv, idle)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("failed to start/close HTTP server [%v]", err)
}
<-idle
log.Info("gracefully shutdown HTTP server")
}
func shutdown(ctx context.Context, srv *http.Server, idle chan struct{}) {
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("shutdown HTTP server by interrupting idle connections [%v]", err)
}
close(idle)
}