应统一使用 chi.Router 注册 /health 端点以确保中间件生效;用 github.com/alexliesenfeld/health 实现标准 JSON 响应,单独建轻量 DB 连接并设超时,禁用动态字段与冗余 Header。

健康检查端点该用 http.HandleFunc 还是 chi.Router?
直接暴露 /health 用原生 http.HandleFunc 最轻量,但如果你项目已用 chi(或 gin、echo)做路由,硬插原生 handler 容易绕过中间件(比如日志、鉴权),导致健康检查日志缺失或被拦截。建议统一走主路由框架。
实操建议:
- 若用
chi:注册时加r.Get("/health", healthHandler),确保路径匹配逻辑和主服务一致 - 避免在 handler 里调
os.Exit(1)或 panic —— 这会让整个进程退出,健康检查失败不该影响服务存活 - 返回状态码必须是
200(正常)或503(不健康),别用404或204,K8s liveness probe 会误判
health.Checker 接口要不要自己实现?
Go 生态里没有标准 health.Checker 接口,go.uber.org/health、github.com/alexliesenfeld/health 等库各自定义。自己写接口看似灵活,实际容易漏掉关键字段(比如 Component 名、Duration、Details map)—— 而 K8s 或 Prometheus 抓取时依赖这些结构。
推荐做法:
立即学习“go语言免费学习笔记(深入)”;
- 直接用
github.com/alexliesenfeld/health,它输出 JSON 符合 OpenAPI Health Check 规范,字段名与 Prometheusprobe_success标签对齐 - 自定义检查项时,用
health.WithChecker("db", dbPingCheck)注册,别把所有逻辑塞进一个函数里 - 注意
dbPingCheck函数必须带超时控制:ctx, cancel := context.WithTimeout(ctx, 2*time.Second),否则 DB 挂了会拖垮整个健康接口
数据库连通性检查为什么总超时?
不是 DB 没连上,而是健康检查用了生产连接池的 db.Ping(),而连接池满或空闲连接被回收后,Ping() 会新建连接并阻塞等待 —— 尤其在低流量时段更明显。
解决方式很具体:
- 单独建一个轻量连接:
sql.Open("postgres", "user=xxx dbname=xxx host=xxx sslmode=disable connect_timeout=2"),不用共享主连接池 - 别复用 ORM 实例(如
gorm.DB)做健康检查,ORM 的DB.Stat()只反映池状态,不验证真实连通性 - 如果用 Redis,用
redisClient.Ping(ctx).Err(),但必须传带超时的ctx,否则默认无超时
如何让健康检查不被 Prometheus 误标为失败?
Prometheus 默认每 10 秒抓一次 /health,但如果响应体含动态时间戳或随机 ID,会导致每次响应 hash 不同,Alertmanager 可能误触发「内容变更告警」。
关键约束:
- 响应 body 必须是稳定 JSON:去掉
"timestamp": "2024-06-12T14:23:01Z"这类字段,或固定为"timestamp": "0001-01-01T00:00:00Z" - HTTP Header 清理:删掉
X-Request-ID、Server等非必要头,只留Content-Type: application/json - 如果用
alexliesenfeld/health,启用health.WithWriterOptions(health.OmitTimestamp())
复杂点在于,有些团队把健康检查和指标暴露混在一个端点,结果 metrics 格式(text/plain)和 health 格式(JSON)冲突。分开部署 /health 和 /metrics 是最省心的做法。


















