go服务不监听信号且未调用http.server.shutdown()即属“猝死”:请求中断、日志截断、事务回滚不全;listenandserve()无信号处理,sigint/sigterm导致defer不执行、连接重置;shutdown()需配context.withtimeout确保收尾时间,并手动关闭所有后台goroutine。

Go 服务不加信号监听和 http.Server.Shutdown(),就不是“关闭”,是“猝死”——请求中断、日志截断、数据库事务回滚不完整,压测时 curl 直接报 Connection reset by peer。
为什么 http.ListenAndServe() 按 Ctrl+C 就挂
它本身是阻塞调用,但完全不响应系统信号。收到 SIGINT 或 SIGTERM 后,进程立刻终止:defer 不执行、TCP 连接被重置、log.Printf() 写到一半就停、db.Exec() 可能已扣款但订单没落库。
-
http.ListenAndServe()没有内置信号处理逻辑,它只管启动并阻塞 - 别指望
os.Exit(0)或return能救场——它们比信号还晚,根本轮不到执行 - 容器环境(如 Kubernetes)发的是
SIGTERM,不是SIGINT,漏监听等于线上不退出
怎么写一个不卡死的 Shutdown() 调用
Shutdown() 本身不阻塞,但它等请求自然结束;没超时控制,就可能永远卡住。关键不在“关”,而在“给请求留够收尾时间”。
- 必须用
context.WithTimeout(context.Background(), 15*time.Second),不能用context.Background()或context.TODO() - 超时时间得大于你最长请求耗时:API 接口 5–10 秒,文件上传或支付回调建议 20–30 秒
- 调用前确保
srv.Listener已关闭(Shutdown()内部会做,但自定义 listener 时建议显式ln.Close()) - 错误只忽略
http.ErrServerClosed,其他如context.DeadlineExceeded得记录
为什么 Shutdown() 返回了,进程还在跑
http.Server.Shutdown() 只管 HTTP 连接,不管你的业务 goroutine。后台消费者、定时器、DB 连接池、Redis 客户端……全得手动关,否则主 goroutine 退出后它们还在跑。
- 所有长期 goroutine 必须用
select { case ,不能只查 <code>if ctx.Err() != nil -
sql.DB要先db.SetMaxOpenConns(1)加速归还连接,再db.Close();redis.Client必须调client.Close() -
time.Ticker得在select里监听ctx.Done()并调ticker.Stop() - WebSocket / SSE 连接不会被
Shutdown()等待,得在 handler 里自己监听ctx.Done()主动断开
最容易被忽略的三个硬伤
不是代码写错,而是底层约束没对齐——哪怕 Shutdown() 跑通,这些点一漏,优雅就破功。
-
http.Server必须设ReadTimeout和WriteTimeout(比如各15 * time.Second),否则慢客户端占着连接不发完数据,Shutdown()就永远等不到它 idle - handler 里所有外部调用必须传 context:
client.Do(req.WithContext(ctx))、db.QueryRowContext(ctx, )、redis.DoWithTimeout() - 信号通道必须带缓冲:
sigChan := make(chan os.Signal, 1),否则并发发两次SIGTERM会丢一次,线上滚动更新直接失败
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











