Go服务健康检查需物理隔离端点:/healthz用独立HTTP服务器和ServeMux,禁用中间件;/readyz同步检查依赖但限时2秒内,DB/Redis等调用须带1500ms超时;两者逻辑、状态、实例完全独立,避免误判与连接堆积。

Go 服务在 Kubernetes 中健康检查并发高,本质不是“检查太多”,而是所有探针请求共享业务 HTTP server 的 goroutine 池和中间件链——一个慢检查会卡住整个服务的响应能力。必须把健康端点从主路由中物理隔离。
用独立 http.ServeMux 注册 /healthz 和 /readyz
别让 /healthz 走 Gin/Echo/Chi 的中间件(比如 JWT 验证、日志、panic 恢复),这些会拖慢甚至阻塞探针。Kubelet 每 5 秒发一次请求,一旦某个 handler 因 DB 超时卡住 2 秒,10 个并发探针就能吃光你设的 http.Server.MaxConns 或耗尽 goroutine 池。
- 启动时单独起一个
http.Server,监听不同端口(如:8081)或同一端口但用独立http.ServeMux - 直接用
http.HandleFunc("/healthz", liveHandler)和http.HandleFunc("/readyz", readyHandler),不经过任何框架封装 - 确保该 mux 不挂任何中间件,也不调用全局 logger、metrics client 等可能阻塞或 panic 的组件
/readyz 必须同步探测依赖,但总耗时要压到 2s 内
/readyz 返回 503 不是失败,而是保护;但它不能慢——Kubernetes 默认 timeoutSeconds: 3,你超时就等于主动触发摘流量。同步检查 DB/Redis/gRPC 是必须的,但得控制节奏。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 所有依赖调用必须带
context.WithTimeout(context.Background(), 1500*time.Millisecond),留 500ms 给序列化和网络抖动 - DB 用
db.PingContext(ctx, 1500*time.Millisecond),不是db.QueryRow;Redis 用client.Ping(ctx).Err(),不是client.Get(ctx, "key") - 非强依赖项(如监控上报、审计日志服务)改用异步检查 + 缓存结果:启动时开 goroutine 定期探测,
/readyz只读缓存值 - 别在 handler 里 new
redis.Client或sql.DB—— 复用初始化好的全局实例,否则每次探针都建连接,文件描述符很快耗尽
避免 /healthz 和 /readyz 共享状态导致误判
常见错误是用同一个原子变量 isReady 控制两个端点,结果 DB 挂了,/healthz 也返回 503,Kubelet 就反复重启容器——而进程本身完全健康,只是下游断了。
-
/healthz只检查进程级快照:runtime.NumGoroutine()是否异常(比如 >5000)、关键 channel 是否已 close、内存是否接近 cgroup limit -
/readyz才做完整依赖链检查,并且失败时响应体必须带具体项,如{"redis": "timeout", "db": "connection refused"},方便快速定位 - 两者 handler 完全独立,不要共用变量、锁或 context;更不要在
/healthz里调用/readyz的逻辑
最易被忽略的一点:Kubernetes 探针默认复用 TCP 连接,如果健康端点没正确设置 http.Server.IdleTimeout 和 ReadTimeout,大量空闲连接会堆积在 TIME_WAIT 状态,最终占满端口或触发连接拒绝。建议显式设 IdleTimeout: 30 * time.Second,并确保 LB 层(如 nginx-ingress、ALB)不复用健康检查连接。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










