健康检查端点应暴露在 /health 或 /healthz 路径,二者最通用且被 kubernetes 及主流代理识别;/health 语义直观,/healthz 可避免中间件劫持;禁止使用 /status 或 /ping 等易混淆或语义不明确的路径。

健康检查端点该暴露在哪个路由路径
Go 服务的健康检查端点没有强制约定,但 /health 和 /healthz 是最通用、被 Kubernetes 和多数反向代理(如 Nginx、Envoy)识别的路径。用 /health 更符合语义直觉;用 /healthz 则能避开某些框架默认注册的 /health(比如某些中间件会劫持它)。避免用 /status 或 /ping——前者易与监控指标混淆,后者无法表达“就绪”或“存活”语义差异。
- 若使用
net/http原生服务,直接http.HandleFunc("/health", ...) - 若用 Gin,注册为
r.GET("/health", healthHandler),不要设成POST或带 query 参数——健康检查必须是无副作用的 GET - Kubernetes 的
livenessProbe和readinessProbe默认都走 HTTP GET,路径需与代码一致,否则探针失败
如何区分 liveness 和 readiness 检查逻辑
很多人把两个探针写成同一个 handler,结果数据库挂了但服务仍被标记为 “ready”,流量继续打入导致雪崩。liveness 表示进程是否还在运行(比如 goroutine 是否卡死),readiness 表示能否接收新请求(比如 DB 连接池是否可用、依赖服务是否响应正常)。
-
/health通常只做轻量级 liveness:检查http.Server是否还在 Accept 连接、主 goroutine 是否 panic 重启过 -
/readyz才做 readiness:尝试执行一次短超时的 DBPing()、调用下游 gRPC 的Check()方法、验证本地缓存是否加载完成 - 不要在
/readyz中做耗时操作(如全量配置重载),超时设为 1–2 秒,Kubernetes 默认探针超时是 1 秒,超时即判失败
用 Gin 或 Echo 配置健康检查时要注意什么
框架自带的中间件可能干扰健康检查路径,比如 JWT 验证中间件会拦截所有请求,导致 /health 返回 401。这不是 bug,是配置顺序问题。
- Gin 中,健康路由必须注册在
Use()全局中间件之前,或显式跳过中间件:r.NoRoute(func(c *gin.Context) { if c.Request.URL.Path == "/health" { healthHandler(c); return } }) - Echo 中,用
e.GET("/health", healthHandler).SkipMiddleware = true(注意不是Use(...).Skip...) - 如果用了 Prometheus 的
promhttp.Handler()暴露指标,别把它和健康检查混在同一个路由组里——/metrics 和 /health 语义不同,不应共享中间件或日志格式
为什么健康检查返回 200 却被 Kubernetes 标记为 Unhealthy
常见原因是响应体内容不符合探测器预期,或 HTTP 状态码被框架悄悄覆盖。Kubernetes 的 httpGet 探针只看状态码,不解析 body,但某些 ingress controller(如 Traefik)或自定义 sidecar 会校验 JSON 字段,比如要求必须有 {"status": "ok"}。
- 确认 handler 显式写了
w.WriteHeader(http.StatusOK),Gin/Echo 默认是 200,但若中间件提前写了 header 就可能出错 - 避免在 handler 里 panic 或 defer recover —— panic 会导致连接直接断开,HTTP 状态码变成 0,K8s 认为连接拒绝
- 用
curl -v http://localhost:8080/health实测,看响应头是否有Content-Length: 0或Transfer-Encoding: chunked异常;某些负载均衡器对空响应体敏感
/readyz 的判断里,不能靠外部定时任务补救。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











