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() ...
}
```
This commit is contained in:
Yaofu 2022-06-15 17:11:19 +08:00 committed by GitHub
parent 05caa5c00e
commit 8d09dce3c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

35
gin.go
View File

@ -5,6 +5,7 @@
package gin package gin
import ( import (
"context"
"fmt" "fmt"
"html/template" "html/template"
"net" "net"
@ -164,6 +165,9 @@ type Engine struct {
maxSections uint16 maxSections uint16
trustedProxies []string trustedProxies []string
trustedCIDRs []*net.IPNet trustedCIDRs []*net.IPNet
// http.Server list for graceful Shutdown
serverList []*http.Server
} }
var _ IRouter = &Engine{} var _ IRouter = &Engine{}
@ -379,7 +383,9 @@ func (engine *Engine) Run(addr ...string) (err error) {
address := resolveAddress(addr) address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address) 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 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.") "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 return
} }
@ -521,7 +529,9 @@ func (engine *Engine) RunUnix(file string) (err error) {
defer listener.Close() defer listener.Close()
defer os.Remove(file) 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 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.") "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 return
} }
@ -585,6 +597,21 @@ func (engine *Engine) HandleContext(c *Context) {
c.index = oldIndexValue 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) { func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method httpMethod := c.Request.Method
rPath := c.Request.URL.Path rPath := c.Request.URL.Path