優雅地關機
優雅關機就是服務端關機命令發出后不是立即關機,而是等待當前還在處理的請求全部處理完畢后再退出程序,是一種對客戶端友好的關機方式。而執行Ctrl+C
關閉服務端時,會強制結束進程導致正在訪問的請求出現問題。
golang實現優雅的關機
package main import ( "fmt" "net/http" "context" "log" "os" "os/signal" "time" "gin-blog/routers" "gin-blog/pkg/setting" ) func main() { router := routers.InitRouter() s := &http.Server{ Addr: fmt.Sprintf(":%d", setting.HTTPPort), Handler: router, ReadTimeout: setting.ReadTimeout, WriteTimeout: setting.WriteTimeout, MaxHeaderBytes: 1 << 20, } go func() { if err := s.ListenAndServe(); err != nil { log.Printf("Listen: %s\n", err) } }() quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) <- quit log.Println("Shutdown Server ...") ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second) defer cancel() if err := s.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown:", err) } log.Println("Server exiting") }
優雅地重啟
優雅關機實現了,那么該如何實現優雅重啟呢?
我們可以使用 fvbock/endless 來替換默認的 ListenAndServe
啟動服務來實現, 示例代碼如下:
package main import ( "log" "net/http" "time" "github.com/fvbock/endless" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/", func(c *gin.Context) { time.Sleep(5 * time.Second) c.String(http.StatusOK, "hello gin!") }) // 默認endless服務器會監聽下列信號: // syscall.SIGHUP,syscall.SIGUSR1,syscall.SIGUSR2,syscall.SIGINT,syscall.SIGTERM和syscall.SIGTSTP // 接收到 SIGHUP 信號將觸發`fork/restart` 實現優雅重啟(kill -1 pid會發送SIGHUP信號) // 接收到 syscall.SIGINT或syscall.SIGTERM 信號將觸發優雅關機 // 接收到 SIGUSR2 信號將觸發HammerTime // SIGUSR1 和 SIGTSTP 被用來觸發一些用戶自定義的hook函數 if err := endless.ListenAndServe(":8080", router); err!=nil{ log.Fatalf("listen: %s\n", err) } log.Println("Server exiting") }
但是需要注意的是,此時程序的PID變化了,因為endless
是通過fork
子進程處理新請求,待原進程處理完當前請求后再退出的方式實現優雅重啟的。所以當你的項目是使用類似supervisor
的軟件管理進程時就不適用這種方式了。