健康检查接口不能只返回{"status": "ok"},因为K8s等系统仅依赖HTTP状态码和响应时效性,不解析JSON内容;必须主动探测下游依赖并返回结构化结果(如DB、Redis状态),失败时返回503及具体错误字段,避免掩盖真实故障。

健康检查接口为什么不能只返回 {"status": "ok"}
很多团队初期用 Gin 写 /health 时直接 c.JSON(200, map[string]string{"status": "ok"}),上线后被 K8s 或 Consul 反复标记为不健康。根本原因是:K8s 的 livenessProbe 和 readinessProbe 默认要求 HTTP 状态码为 200 且响应体可解析,但更关键的是——它会校验响应时间、超时行为和错误传播逻辑。纯静态响应掩盖了数据库连接、缓存连通性等真实依赖状态。
- 必须主动探测下游依赖(如
redis.Ping()、db.Exec("SELECT 1")),失败时返回 5xx 并带具体错误字段 - 响应体建议用结构体而非 map,方便后续加字段(如
timestamp、version) - 避免在 handler 里做耗时操作(如调用外部 HTTP 接口),否则 probe 超时导致误判
gin.HandlerFunc 中如何安全执行多依赖检查并聚合结果
别把所有 ping 操作串行写在 handler 里。Gin 的 handler 是同步阻塞的,一个依赖超时会拖垮整个探针。正确做法是并发发起检查,用 context.WithTimeout 控制单个依赖上限,并统一收集错误。
/readyz
-
/readyz应检查所有依赖(DB、Redis、下游 HTTP 服务),任一失败即 503 -
/livez只检查进程自身是否存活(如 goroutine 泄漏、内存爆满),甚至可简化为只返回 200 - 绝对不要给这两个路由加
authMiddleware或rateLimit,probe 请求必须绕过所有业务中间件
Gin 注册探针路由时容易忽略的三个配置点
很多人注册完 GET /health 就以为完事了,但 K8s 探针实际发请求时会暴露隐藏问题:
- 没禁用 Gin 的
Recovery中间件:当某个依赖 panic(如空指针调用redis.Client),默认 Recovery 会吞掉 panic 并返回 500,但 K8s 无法区分这是临时错误还是进程已崩溃,建议对探针路由单独移除 Recovery:r.NoRoute(func(c *gin.Context) { /* ... */ })不适用,应显式跳过 - 没设置
Content-Type: application/json:某些旧版 Ingress(如 Nginx 0.2x)会因缺少 header 强制转码,导致 JSON 解析失败,务必显式写c.Data(200, "application/json", data) - 没处理 HEAD 请求:K8s probe 默认发 GET,但部分平台(如 OpenShift)可能发 HEAD,Gin 默认不响应 HEAD,需额外注册:
r.HEAD("/readyz", healthCheckHandler)
真正难的不是写通接口,而是让每个依赖的失败能被准确归因、不引发级联故障。比如 DB 连接池耗尽时,要区分是连接数不够(该扩容),还是慢查询堆积(该优化 SQL),这些都得靠探针返回的结构化错误字段来定位。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











