I am trying to find out if there is any difference between two implementations that currently do the same thing - gracefully shutdown applications/servers, for example, when Ctrl+C is hit. Both work fine and are based on the documentation.
What a friend of mine says, is that Example 2 handles shutdown at the application level which shuts down all contexts throughout the application. However, Example 1 does it at the 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 the 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 the application's main.go file so the whole graceful shutdown has been split within two files. The only addition is this example uses the 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)
}