From 8d09dce3c2fc0d663c26fedb5079f2e049767069 Mon Sep 17 00:00:00 2001 From: Yaofu Date: Wed, 15 Jun 2022 17:11:19 +0800 Subject: [PATCH] feat(gracefulstop): add wrapper Shutdown We start httpServer by gin as example below In my project. Here is a case: 1. Start gin http server 2. Receive data from gin http server and send task to worker to process. 3. The worker stop at first when my process go to exit. But my gin http server is running, some error happened. We expect to stop gin http server at first by calling func 'http.Server.Shutdown(context.TODO())' to avoid process error. So, can i make a mr and add wrapper func 'Shutdown()' to gin for graceful stop my process. Here are my use case. ```golang func main() { // Set up a http server. r := gin.Default() // ... go func() { // Run http server if err := r.Run(":8899"); err != nil { log.Fatalf("could not run server: %v", err) } }() // Run worker go RunWorker() // wait signal to stop WaitSignal() } // RunWorker run worker in one goroutine func RunWorker() { for !isStop { // ... some request received by gin http server ... // ... do some work ... } } // WaitSignal stop signal handle func WaitSignal() { shutdownHook := make(chan os.Signal, 1) signal.Notify(shutdownHook, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, os.Interrupt) <-shutdownHook GracefulStop() // wait 2 second for graceful stop. time.Sleep(2 * time.Second) os.Exit(0) } // GracefulStop graceful stop http.Server at first, then stop all workers. func GracefulStop() { // 1. stop http.Server and refuse new data are received. err := server.Shutdown(context.TODO()) // 2. stop worker and finish tasks in process. // StopWorker() ... } ``` --- gin.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/gin.go b/gin.go index f9324299..051d3337 100644 --- a/gin.go +++ b/gin.go @@ -5,6 +5,7 @@ package gin import ( + "context" "fmt" "html/template" "net" @@ -164,6 +165,9 @@ type Engine struct { maxSections uint16 trustedProxies []string trustedCIDRs []*net.IPNet + + // http.Server list for graceful Shutdown + serverList []*http.Server } var _ IRouter = &Engine{} @@ -379,7 +383,9 @@ func (engine *Engine) Run(addr ...string) (err error) { address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) - err = http.ListenAndServe(address, engine.Handler()) + server := &http.Server{Addr: address, Handler: engine.Handler()} + engine.serverList = append(engine.serverList, server) + err = server.ListenAndServe() return } @@ -498,7 +504,9 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") } - err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler()) + server := &http.Server{Addr: addr, Handler: engine.Handler()} + engine.serverList = append(engine.serverList, server) + err = server.ListenAndServeTLS(certFile, keyFile) return } @@ -521,7 +529,9 @@ func (engine *Engine) RunUnix(file string) (err error) { defer listener.Close() defer os.Remove(file) - err = http.Serve(listener, engine.Handler()) + server := &http.Server{Handler: engine.Handler()} + engine.serverList = append(engine.serverList, server) + err = server.Serve(listener) return } @@ -558,7 +568,9 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) { "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") } - err = http.Serve(listener, engine.Handler()) + server := &http.Server{Handler: engine.Handler()} + engine.serverList = append(engine.serverList, server) + err = server.Serve(listener) return } @@ -585,6 +597,21 @@ func (engine *Engine) HandleContext(c *Context) { c.index = oldIndexValue } +// Shutdown is Wrapper for http.Server.Shutdown(ctx) +// Once Shutdown has been called on a server, it may not be reused; +// future calls to methods such as Serve will return ErrServerClosed. +func (engine *Engine) Shutdown(ctx context.Context) error { + for _, srv := range engine.serverList { + if srv == nil { + continue + } + if err := srv.Shutdown(ctx); err != nil { + return err + } + } + return nil +} + func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path