健康检查接口需严格区分liveness与readiness:liveness仅验证进程HTTP响应能力,readiness才检查DB等依赖;状态码必须显式设置且正确(如readiness失败用503),避免误用500导致探针误判;数据库检查应复用连接池、加超时、并行执行,防止阻塞和雪崩。

健康检查接口不是“能返回 200 就算完”,Kubernetes、Consul 等系统只看 HTTP 状态码,不解析响应体;返回 200 但数据库已断连,等于主动把流量引向故障节点。
为什么 /health 返回 200 却被 Kubernetes 认为不健康
常见错误是没设状态码就调用 http.Error(w, "msg", http.StatusServiceUnavailable)——Go 的 http.Error 默认用 500 Internal Server Error,而 readiness 探针收到非 2xx 状态码(比如误触发的 500)会直接判定失败。
- 必须显式写
w.WriteHeader(http.StatusServiceUnavailable)或用http.Error(w, "...", http.StatusServiceUnavailable),且确保传入的是目标状态码 - Kubernetes 不校验 JSON 字段,
{"status":"ok"}和空字符串""在 200 下效果完全一样;但若返回 200 +{"status":"down"},照样被当健康 - 路径名无强制要求:
/health、/readyz、/livez都可以,但 K8s YAML 中的livenessProbe.httpGet.path必须与 handler 注册路径严格一致
如何安全检查数据库连接而不打爆连接池
直接在 handler 里调 db.Ping() 是高危操作:底层可能新建连接,高频请求会导致连接数飙升、超时堆积。真正该复用的是连接池当前状态 + 轻量 SQL。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 先查
db.Stats().OpenConnections > 0,快速过滤掉池已空的情况 - 再执行
db.QueryRowContext(ctx, "SELECT 1"),ctx必须带超时(如2 * time.Second),绝不能用context.Background() - 别在 liveness 接口中做这个——DB 挂了但进程活着,liveness 失败会触发重启雪崩;只放在 readiness 中
为什么 livenessProbe 配太短会导致服务反复重启
Go 默认 http.Server 没设 ReadTimeout 和 WriteTimeout,一次 GC STW 或日志刷盘卡顿几秒,就可能让整个 HTTP 处理阻塞。此时若探针 periodSeconds: 5、timeoutSeconds: 1,连续失败后 K8s 会 kill + restart 容器。
- 务必初始化 server 时设置:
ReadTimeout: 5 * time.Second、WriteTimeout: 10 * time.Second -
initialDelaySeconds至少为冷启动耗时 + 2 秒,例如 Gin 加载配置+路由约 3 秒,那就设 5 - liveness 只检查进程是否可响应 HTTP,不做任何依赖探测;readiness 才负责 DB、Redis、下游 gRPC 是否可用
最易被忽略的一点:所有检查项必须并行执行且受统一 context 控制,单个慢依赖(比如 Redis 响应 3 秒)不应拖垮整个健康接口——否则 readiness 超时,K8s 会立刻摘流量,哪怕其他依赖全好。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










