Golang 从入门到放弃 -0x19
优雅关闭与服务生命周期:信号处理、Shutdown 和收尾动作。
很多服务写到最后,大家最容易忽略的一件事,就是它怎么停。开发时直接 Ctrl+C 一按当然也能结束,但真实环境里,一个服务如果不能优雅关闭,轻则日志难看,重则请求丢一半、连接没收尾、数据写一半。
什么叫优雅关闭
简单说,就是收到退出信号以后,不是立刻暴毙,而是给自己一点收尾时间:
- 停止接新请求
- 让正在处理的请求尽量跑完
- 关闭数据库连接、消息消费者、后台任务
监听系统信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
fmt.Println("shutting down")在 Linux 环境里,这两个信号特别常见。容器停止、手动结束进程,往往都绕不开它们。
HTTP 服务的 Shutdown
srv := &http.Server{
Addr: ":8080",
Handler: mux,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Println("shutdown error:", err)
}Shutdown 会停止接收新连接,并尽量等已有请求处理完。和直接 Close() 相比,它明显更像个正常人。
后台 goroutine 也别忘了
很多程序除了 HTTP 服务,还有定时任务、消息消费、异步 worker。别光把 Web 口停了,后台 goroutine 还在无头苍蝇一样乱跑。
这时候前面学过的 context 就很重要了。统一传一个可取消的 ctx,退出时一起通知大家收工。
优雅关闭和部署关系很大
比如容器平台给你 10 秒收尾时间,你代码里就别只给自己 1 秒;反过来,如果平台 5 秒就强杀,你却幻想自己有半分钟慢慢整理情绪,那也不现实。
代码里的超时策略,最好和部署环境配套想,而不是各写各的。
小结
这一章不是功能开发,但很影响服务的“品相”:
- 优雅关闭的核心是先停接入,再做收尾。
- 监听信号是入口,
Shutdown是关键动作。 - 后台任务和 goroutine 别漏掉。
- 关闭超时要结合真实部署环境来定。
下一章我们讲性能分析和排查。Go 代码不是写完就永远跑得快,真慢的时候,你得知道该从哪儿下手。