健康检查必须区分 /livez(仅检查进程存活)和 /readyz(检查业务依赖),并合理缓存探测结果、设置超时与状态码;consul 需显式配置 health_check 和 deregistercriticalserviceafter;返回体应轻量、结构统一、避免高危操作。

Go 服务的健康检查不是“加个 /health 接口就完事”,它直接决定 Kubernetes 是否重启 Pod、Consul 是否转发流量、Nginx 是否摘除节点——错配语义或逻辑,轻则误杀,重则雪崩。
为什么 /healthz 和 /readyz 必须分开写
很多人用同一个 handler 同时应付 livenessProbe 和 readinessProbe,结果一次数据库抖动就触发连续重启。Kubernetes 的 livenessProbe 只需要确认进程没卡死、HTTP server 还在 accept 连接;而 readinessProbe 要判断服务是否真能处理请求,比如 DB 连得上、Redis 可写、下游 HTTP 服务有响应。
-
/livez:只检查http.Server是否存活、关键 channel 是否阻塞、goroutine 数是否异常暴涨 —— 不碰任何外部依赖 -
/readyz:才该调db.PingContext、redis.Ping、http.Get等,且必须带context.WithTimeout(超时设为 probetimeoutSeconds的 60%) - 不分离?K8s 默认把未定义
/readyz的服务当成“一启动就 ready”,但配置还没加载完、缓存还没预热,流量进来就 panic
Consul 注册后服务始终显示 “critical” 怎么办
注册成功 ≠ 健康。Consul 默认认为所有服务“不健康”,除非你显式配置 health_check 字段。哪怕只配了个 TCP 检查,也比不配强——但 TCP 检查根本验不了业务逻辑,DB 连不上、Redis 雪崩时端口照样通。
- 必须在
api.AgentServiceRegistration中补全Checks字段,例如 HTTP 探针:HTTP: "http://127.0.0.1:8080/healthz" - 别漏掉
DeregisterCriticalServiceAfter:设成"90s",否则服务挂了 Consul 感知不到,下游持续调用失败 - 确保本机运行 Consul Agent,Client 初始化地址必须是
"http://127.0.0.1:8500",不是公网地址或 DNS 名 - 返回体里建议带明细,如
{"db": "failed", "redis": "ok"}—— Consul 不消费它,但运维排查时省半小时
handler 里写 db.Ping 就是埋雷
高频轮询(比如每 2 秒一次)下,每次请求都执行 db.PingContext,不仅拖慢响应,还会对数据库造成脉冲压力。更糟的是,livenessProbe 超时默认仅 1 秒,一旦 DB 网络抖动,K8s 就判定“不存活”,反复 kill + restart。
- 正确做法:启动一个后台 goroutine,按固定间隔(如 5 秒)执行全量依赖检查,结果缓存在内存中
-
/readyzhandler 只读快照,不真实探测 —— 用sync.RWMutex保护共享结构体 - 别在 handler 里做
log.Fatal或未 recover 的 panic,那等于主动让整个进程退出 - 返回状态码必须是
http.StatusOK,不能用http.StatusNoContent或http.StatusServiceUnavailable,探测器只认 200
返回 JSON 还是纯文本 OK?看谁在调
Kubernetes 的 livenessProbe 只看 HTTP 状态码,不要 body;但人类运维和 Prometheus Exporter 需要可读信息。折中方案是:保持状态码为 200,body 返回轻量 JSON,且设 Content-Type: application/json。
- 推荐结构:
{"status":"UP","uptime_sec":12345},字段名统一小写,值用"UP"/"DOWN",别用"ok"或"healthy" - 别返回大 JSON(比如 dump 整个
runtime.MemStats),某些旧版 kubelet 解析会超时或失败 - 如果只给机器看(如 Docker HEALTHCHECK),用纯文本
"OK"更稳妥,Content-Type设为text/plain - 严禁在 handler 里调
exec.Command或读/proc—— 权限风险高、性能差,改用runtime.ReadMemStats
最常被忽略的一点:健康检查不是“上线前配好就一劳永逸”的事情。它得跟你的部署方式(K8s vs Consul vs Nginx)、依赖拓扑(DB 是单点还是集群)、甚至 GC 峰值时间都有关联。一次没设超时的 http.Get,可能在压测时才暴露出来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











