golang的平滑关闭
最近在看公司的一个go项目,发现一段有意思的代码,代码大致的结构如下:
func main() { engine := gin.Default() engine.Handle("GET", "/hello", func(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "err": 0, "data": []string{}, }) time.Sleep(5 * time.Second) }) server := http.Server{ Addr: ":8081", Handler: engine, } go func() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) <-c ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) err := server.Shutdown(ctx) if err != nil { cancel() } }() err := server.ListenAndServe() if err == nil { fmt.Println("error") } }
在这段代码中,使用子协程去监听关闭通知。这显然是不能达到代码写者想要达到的效果的。原因在于当主协程收到退出信号时会直接退出,而没有去关注子协程的状态,而在go语言中,主协程一旦退出,子协程也就退出了,所以并不能达到平滑关闭的效果。
如果要达到平滑关闭的效果:
方法一:需要将ListenAndServe放到子协程中,而在主协程中监听退出信号。
func main() { engine := gin.Default() engine.Handle("GET", "/hello", func(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "err": 0, "data": []string{}, }) time.Sleep(5 * time.Second) }) server := http.Server{ Addr: ":8081", Handler: engine, } go func() { err := server.ListenAndServe() if err == nil { fmt.Println("error") } }() c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) <-c ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) err := server.Shutdown(ctx) if err != nil { cancel() } }
方法二:同步子协程的状态给主协程让其等待子协程处理完成。
func main() { engine := gin.Default() engine.Handle("GET", "/hello", func(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "err": 0, "data": []string{}, }) time.Sleep(5 * time.Second) }) server := http.Server{ Addr: ":8081", Handler: engine, } var wg sync.WaitGroup wg.Add(1) go func() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) <-c ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) err := server.Shutdown(ctx) if err != nil { cancel() } wg.Done() }() err := server.ListenAndServe() if err == nil { fmt.Println("error") } wg.Wait() }