go服务的/health接口必须检查真实依赖状态而非仅返回{"status":"ok"},包括数据库连接、redis可写性、下游http服务响应、磁盘空间等,并区分/health与/ready用途,使用带超时的context避免阻塞。

Go 服务的 /health 接口不该只返回 {"status": "ok"} —— 它必须反映真实依赖状态,否则在 Kubernetes 或负载均衡器里会掩盖故障。
健康检查该查什么:不是“进程活着”,而是“能提供服务”
单纯检查 HTTP 服务是否可连通(比如用 http.ListenAndServe 启动成功)毫无意义。真正的健康检查要覆盖关键依赖:
-
数据库连接池是否能获取连接(执行db.PingContext(ctx),而非仅检查db != nil) -
Redis实例是否可写(redisClient.Set(ctx, "health:probe", "1", 1*time.Second).Err()) - 下游核心
HTTP服务是否响应超时可控(带context.WithTimeout的http.Client.Do) - 本地磁盘剩余空间是否低于阈值(
syscall.Statfs检查Avail)
不检查这些,K8s 的 livenessProbe 可能永远不重启挂死的服务,readinessProbe 会把流量导给无法处理请求的实例。
使用 net/http 实现轻量级 /health 路由
避免引入额外框架(如 Gin 的中间件链),直接用标准库保证启动快、无副作用:
func healthHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
status := map[string]any{"status": "ok", "timestamp": time.Now().UTC().Format(time.RFC3339)}
// 检查 DB
if err := db.PingContext(ctx); err != nil {
status["db"] = map[string]string{"error": err.Error()}
status["status"] = "degraded"
}
// 检查 Redis
if err := redisClient.Set(ctx, "health:probe", "1", time.Second).Err(); err != nil {
status["redis"] = map[string]string{"error": err.Error()}
status["status"] = "degraded"
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(status)
}
注意:db.PingContext 和 redisClient.Set 都必须传入带超时的 ctx,否则一次卡住的 DB 查询会让整个健康检查阻塞数秒。
区分 /health 和 /ready:K8s 场景下必须拆开
Kubernetes 要求 livenessProbe 和 readinessProbe 行为不同,硬塞进一个端点会导致误判:
-
/health(或/live)只检查自身进程和基础依赖(DB 连通性),失败即kill -9重启 -
/ready必须额外检查业务就绪条件(例如配置热加载完成、gRPC server 已注册所有服务、本地缓存预热完毕),失败则从 Service Endpoints 中摘除,但不重启
示例中若配置中心不可用,/ready 应返回 503,但 /health 仍可返回 200 —— 否则会触发无意义重启。
别忽略响应头和超时:负载均衡器会按规则丢弃请求
Nginx、ALB、Cloud Load Balancing 默认对健康检查有严格限制:
- 响应必须在
1~5 秒内返回(AWS ALB 默认超时 5s),所以每个依赖检查需设context.WithTimeout(ctx, 2*time.Second) - 必须返回
Content-Type: application/json,否则某些 LB 会认为探测失败 - 避免返回重定向(302)、非 2xx 状态码(除非确实不健康),否则 K8s 会持续重启
- 不要在
/health里记录日志或打 metric —— 它每秒可能被调用数十次,容易刷爆日志系统
最常被忽略的是上下文超时传递:写 db.Ping() 而非 db.PingContext(ctx),一旦数据库连接卡住,整个健康检查协程就 hang 住,最终拖垮整个服务的就绪判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











