应分「准备阶段」和「就绪阶段」:先完成数据库连接、配置加载、缓存预热等初始化,再启动监听;健康检查端点仅在所有依赖就绪后返回200;优雅关闭需监听SIGTERM、使用context控制goroutine退出、调用各组件Shutdown/Close并设超时。

Go 服务启动时如何避免「监听已就绪但业务未初始化」?
服务启动后立刻开始接受请求,但数据库连接、配置加载、缓存预热等还没完成,会导致 500 或空响应。这不是 http.ListenAndServe 的问题,而是控制权交出太早。
- 启动流程必须拆成「准备阶段」和「就绪阶段」:先做初始化,再开监听
- 推荐用
sync.Once或状态机变量(如ready atomic.Bool)控制就绪信号 - HTTP 健康检查端点(如
/healthz)应只返回 200 当且仅当所有依赖已 ready - 不要依赖「监听端口成功」作为服务可用标志——端口通 ≠ 服务可用
收到 SIGTERM 后为什么 goroutine 还在跑?
Go 默认不中断正在运行的 goroutine。os.Signal 只是通知你“该停了”,后续逻辑全靠自己写。
- 必须显式监听
os.Interrupt和syscall.SIGTERM,用signal.Notify注册 - 所有长期运行的 goroutine(比如消息消费、定时任务)需接收
context.Context并在<ctx>.Done()</ctx>触发时退出 - 切忌在
main函数里直接os.Exit(0)—— 这会跳过 defer 和 cleanup - 关键资源(DB 连接池、HTTP server、Kafka consumer)要调用各自的
Close()或Shutdown()方法,且带超时(如srv.Shutdown(ctx))
http.Server.Shutdown() 总是超时失败?
常见现象是日志里出现 context deadline exceeded,然后进程被强制 kill。根本原因是还有活跃连接没结束,或 handler 卡死没响应 ctx.Done()。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
Shutdown()不会杀连接,只发通知并等待 handler 自行退出 - handler 内部必须用
ctx控制子操作:比如db.QueryContext(ctx, ...)、time.Sleep改为time.AfterFunc+select监听ctx.Done() - 启动时给
http.Server设置ReadTimeout、WriteTimeout、IdleTimeout,防止慢连接拖死 shutdown - 调用
Shutdown()前建议先关闭 listener(ln.Close()),避免新连接进来
第三方库(如 gRPC、Redis client)怎么一起优雅停?
它们各自有自己的生命周期管理方式,混用时容易漏关或顺序错。
- gRPC Server:调用
grpcServer.GracefulStop()(不是Stop()),它内部会等所有 RPC 完成 - Redis client(如
github.com/redis/go-redis/v9):调用client.Close(),它会等待 pending 命令完成并释放连接 - Kafka consumer(sarama):需调用
consumer.Close(),并确保没有 goroutine 在持续Consume() - 所有 Close/Shutdown 操作建议串行执行,并加日志确认:“closing X… done”,方便排查哪一步卡住
真正难的不是写 shutdown 逻辑,而是每个业务 handler 是否真尊重 context、每个外部 client 是否提供了可等待的关闭接口、以及测试时是否模拟过「请求中突然收 SIGTERM」这种场景。这些地方一漏,优雅就只剩个名头。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










