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()
}

浙公网安备 33010602011771号