必须用http.Server.Shutdown()配合带超时的context.Context;它先拒绝新连接,再等待已有handler返回或超时,而Close()或忽略信号会立即中断所有连接,导致上传失败、流响应截断。

必须用 http.Server.Shutdown() 配合带超时的 context.Context
直接调 http.Server.Close() 或放任 http.ListenAndServe() 被 SIGTERM 中断,都会立刻关闭监听器并强制断开所有连接——上传中断、长轮询失败、流式响应截断。真正的优雅停机只有一条路径:srv.Shutdown(ctx),它先拒绝新连接,再等待已有 handler 返回(或超时)。ctx 必须带超时,比如 context.WithTimeout(context.Background(), 25*time.Second);传 context.Background() 等于没设限,进程可能永远 hang 住。
信号监听必须带缓冲通道 + 显式接收
常见错误是只调 signal.Notify(sigChan, syscall.SIGTERM) 就结束,没写接收逻辑,main 函数跑完进程直接退出,根本等不到信号。必须:
-
sigChan := make(chan os.Signal, 1)—— 缓冲为 1,防丢信号 - 显式阻塞接收:
或for range sigChan - 启动
srv.ListenAndServe()必须放在 goroutine 里,否则主 goroutine 卡死,收不到信号 - 只监听
syscall.SIGTERM(K8s 默认发)和syscall.SIGINT(本地调试),别加SIGHUP等无关信号
HTTP 之外的资源必须手动关,且顺序重要
http.Server.Shutdown() 只管 HTTP 连接层,gRPC、DB、Redis、WebSocket、定时器全得自己收尾。漏一个,进程就卡住:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- gRPC Server:用
grpcServer.GracefulStop(),不是Stop() - 数据库(
*sql.DB):调db.Close(),它会等活跃连接归还 - Redis client(如
redis.Client):调client.Close(),释放底层连接 - WebSocket 连接:需维护连接池(如
sync.Map),收到 shutdown 信号后遍历调conn.Close() - 后台 goroutine(如 metrics 上报):必须在
select中监听ctx.Done(),不能用time.Sleep硬等
K8s 环境下必须对齐 terminationGracePeriodSeconds 和 Shutdown 超时
K8s 删除 Pod 时,给容器的总宽限期由 terminationGracePeriodSeconds 决定(默认 30 秒)。Go 代码里的 Shutdown() 超时必须比它小 5–10 秒,比如设成 20–25 秒。否则 K8s 在超时后直接发送 SIGKILL 强杀,你的清理逻辑根本没机会执行完。同时确认容器 command 是 ["./myapp"],而非 ["sh", "-c", "./myapp"] —— 后者让 shell 成为 PID 1,不转发信号给子进程。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










