From 8ebf4a26f46867dbc76e2dc35aab0a6dad25afc5 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 3 Aug 2021 16:40:33 -0600 Subject: gracefully shutdown on sigint/sigterm --- srv/cmd/mediocre-blog/main.go | 65 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/srv/cmd/mediocre-blog/main.go b/srv/cmd/mediocre-blog/main.go index b94d405..f912f45 100644 --- a/srv/cmd/mediocre-blog/main.go +++ b/srv/cmd/mediocre-blog/main.go @@ -2,14 +2,20 @@ package main import ( "context" + "errors" "flag" "fmt" + "net" "net/http" "net/http/httputil" "net/url" + "os" + "os/signal" "path" "strconv" "strings" + "syscall" + "time" "github.com/emersion/go-sasl" "github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist" @@ -25,10 +31,17 @@ func loggerFatalErr(ctx context.Context, logger *mlog.Logger, descr string, err func main() { + ctx := context.Background() + logger := mlog.NewLogger(nil) + defer logger.Close() + + logger.Info(ctx, "process started") + defer logger.Info(ctx, "process exiting") publicURLStr := flag.String("public-url", "http://localhost:4000", "URL this service is accessible at") - listenAddr := flag.String("listen-addr", ":4000", "Address to listen for HTTP requests on") + listenProto := flag.String("listen-proto", "tcp", "Protocol to listen for HTTP requests with") + listenAddr := flag.String("listen-addr", ":4000", "Address/path to listen for HTTP requests on") dataDir := flag.String("data-dir", ".", "Directory to use for long term storage") staticDir := flag.String("static-dir", "", "Directory from which static files are served (mutually exclusive with -static-proxy-url)") @@ -46,45 +59,46 @@ func main() { switch { case *staticDir == "" && *staticProxyURLStr == "": - logger.Fatal(context.Background(), "-static-dir or -static-proxy-url is required") + logger.Fatal(ctx, "-static-dir or -static-proxy-url is required") case *powSecret == "": - logger.Fatal(context.Background(), "-pow-secret is required") + logger.Fatal(ctx, "-pow-secret is required") case *smtpAddr == "": - logger.Fatal(context.Background(), "-ml-smtp-addr is required") + logger.Fatal(ctx, "-ml-smtp-addr is required") case *smtpAuthStr == "": - logger.Fatal(context.Background(), "-ml-smtp-auth is required") + logger.Fatal(ctx, "-ml-smtp-auth is required") } publicURL, err := url.Parse(*publicURLStr) if err != nil { - loggerFatalErr(context.Background(), logger, "parsing -public-url", err) + loggerFatalErr(ctx, logger, "parsing -public-url", err) } var staticProxyURL *url.URL if *staticProxyURLStr != "" { var err error if staticProxyURL, err = url.Parse(*staticProxyURLStr); err != nil { - loggerFatalErr(context.Background(), logger, "parsing -static-proxy-url", err) + loggerFatalErr(ctx, logger, "parsing -static-proxy-url", err) } } powTargetUint, err := strconv.ParseUint(*powTargetStr, 0, 32) if err != nil { - loggerFatalErr(context.Background(), logger, "parsing -pow-target", err) + loggerFatalErr(ctx, logger, "parsing -pow-target", err) } powTarget := uint32(powTargetUint) smtpAuthParts := strings.SplitN(*smtpAuthStr, ":", 2) if len(smtpAuthParts) < 2 { - logger.Fatal(context.Background(), "invalid -ml-smtp-auth") + logger.Fatal(ctx, "invalid -ml-smtp-auth") } smtpAuth := sasl.NewPlainClient("", smtpAuthParts[0], smtpAuthParts[1]) smtpSendAs := smtpAuthParts[0] // initialization - ctx := mctx.Annotate(context.Background(), + ctx = mctx.Annotate(ctx, "publicURL", publicURL.String(), + "listenProto", *listenProto, "listenAddr", *listenAddr, "dataDir", *dataDir, "powTarget", fmt.Sprintf("%x", powTarget), @@ -164,7 +178,32 @@ func main() { logger.Info(ctx, "listening") - // TODO graceful shutdown - err = http.ListenAndServe(*listenAddr, mux) - loggerFatalErr(ctx, logger, "listening", err) + l, err := net.Listen(*listenProto, *listenAddr) + if err != nil { + loggerFatalErr(ctx, logger, "creating listen socket", err) + } + + srv := &http.Server{Handler: mux} + go func() { + if err := srv.Serve(l); err != nil && !errors.Is(err, http.ErrServerClosed) { + loggerFatalErr(ctx, logger, "serving http server", err) + } + }() + + defer func() { + closeCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + logger.Info(ctx, "beginning graceful shutdown of http server") + + if err := srv.Shutdown(closeCtx); err != nil { + loggerFatalErr(ctx, logger, "gracefully shutting down http server", err) + } + }() + + sigCh := make(chan os.Signal) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + <-sigCh + + // let the defers begin } -- cgit v1.2.3