Go 后端优雅关机:signal + context,让服务“体面地”停下来

举报
yd_278073926 发表于 2026/11/11 06:06:25 2026/11/11
【摘要】 Go 后端优雅关机:signal + context,让服务“体面地”停下来部署过 Go 服务的人,大概都遇到过这样的场景:正在处理一个下单请求,运维一键重启,进程被 SIGTERM 直接杀掉,请求做到一半就断了,用户看到 502,订单状态还卡在中间态。问题不在重启本身,而在关机的方式太粗暴——进程没有机会把手头的事做完。优雅关机要解决的其实就一件事:收到停机信号后,先停掉新流量,再等正在...

Go 后端优雅关机:signal + context,让服务“体面地”停下来

部署过 Go 服务的人,大概都遇到过这样的场景:正在处理一个下单请求,运维一键重启,进程被 SIGTERM 直接杀掉,请求做到一半就断了,用户看到 502,订单状态还卡在中间态。问题不在重启本身,而在关机的方式太粗暴——进程没有机会把手头的事做完。

优雅关机要解决的其实就一件事:收到停机信号后,先停掉新流量,再等正在处理的请求收尾,最后才退出。Go 标准库已经把这套机制准备好了,关键是把它串起来。

第一步:接住信号,别让进程“猝死”

Linux 下 systemctl stop 发的是 SIGTERM,Ctrl+C 发的是 SIGINT。默认行为是立即终止进程。正确做法是用 signal.Notify 把这两个信号接住,转成一个 channel:

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

收到信号只是开始,真正的工作在后面。注意 channel 要带 1 的缓冲,否则信号可能在主 goroutine 忙的时候丢掉。

第二步:停掉新流量,再等旧请求

http.Server 提供了 Shutdown(ctx):它先关闭监听端口,拒绝新连接,然后等已建立的连接处理完再返回。配合一个带超时的 context,就能给收尾工作定一个上限:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
    log.Printf("强制关闭: %v", err)
}

10 秒这个数字要根据业务定。点餐接口一般几百毫秒就能完成,10 秒足够;如果有长轮询或大文件上传,就要放宽,或者在 handler 里监听 r.Context().Done() 主动中断。

第三步:收尾的不只是 HTTP

一个真实服务里需要收尾的东西往往不止 HTTP 连接:数据库连接池要 Close(),后台定时任务要停掉,消息队列的消费者要提交 offset 再退出。做法是把这些清理动作都注册到一个统一的 shutdown 流程里,按依赖顺序倒序执行——先停消费者,再关连接池。顺序错了,消费者可能在连接池关闭后还尝试写库,直接报错。

一个常被忽略的细节:健康检查

如果服务前面挂了负载均衡,只靠 Shutdown 还不够:关机信号发出后,负载均衡可能还不知道这个节点要下线,继续把新请求打过来。稳妥的做法是在收到信号后,先让健康检查接口返回不健康,等几秒让负载均衡把节点摘掉,再执行 Shutdown。这几秒的延迟换来的是零 502,值得。

超时之后怎么办

Shutdown 的 context 超时后,剩下的连接会被强制关闭。超时不是失败,而是兜底——它保证进程一定能在有限时间内退出,不会因为一个挂住的请求永远停不下来。systemd 的 TimeoutStopSec 也要和这里对齐:如果代码里给 10 秒,systemd 默认给 90 秒,进程早已退出,不会有冲突;反过来如果代码里等 120 秒而 systemd 只给 90 秒,进程会被 SIGKILL 强杀,优雅关机就白做了。

小结

优雅关机 = 接信号 + 停新流量 + 等旧请求 + 按序清理 + 超时兜底。代码量不到五十行,却是区分“能跑”和“能放心重启”的分界线。部署脚本里多写这几行,下次半夜重启服务,就不用再盯着错误日志提心吊胆了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。