健康检查接口必须返回200 OK状态码,因负载均衡器和K8s livenessProbe默认仅认200为存活;不可混用就绪检查或在其中执行耗时依赖检测,应通过异步预检+缓存保障毫秒级响应。

健康检查接口该返回什么状态码
健康检查接口必须返回 200 OK,不能用 204 No Content 或 302 之类的状态。很多负载均衡器(如 AWS ALB、Nginx Plus)和 K8s livenessProbe 默认只认 200 才判定为“存活”。哪怕你返回 JSON {"status": "ok"} 却配了 503,服务就会被反复重启。
常见错误:把健康检查和“就绪检查”混用,或在健康检查里偷偷查数据库连接——一旦 DB 挂了,livenessProbe 失败触发重启,反而加剧雪崩。
用 net/http 写最简健康检查 handler
不需要引入任何第三方框架,标准库一行就能写清楚:
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
注意点:
立即学习“go语言免费学习笔记(深入)”;
-
w.WriteHeader(http.StatusOK)必须显式调用,否则默认是200,看似没问题,但某些严格校验的探针会要求 header 明确声明 - 避免用
fmt.Fprint,它可能隐式写入额外空格或换行,某些客户端解析失败 - 不要在 handler 里做耗时操作(如调用
time.Sleep模拟检测),K8s 默认超时是 1 秒,超时即判为失败
需要检查依赖时怎么设计不拖垮主路径
真正的生产健康检查常需验证数据库、Redis、下游 HTTP 服务是否可达。但这些检查不能阻塞主健康接口——否则一个 Redis 延迟,整个服务就被标记为不健康。
推荐做法是异步预检 + 缓存结果:
- 启动时开 goroutine 定期(如每 10 秒)探测依赖,结果存进内存变量(如
atomic.Value或带锁 map) -
/healthhandler 只读缓存,毫秒级返回 - 加个
/healthz?verbose=1作为调试端点,手动触发实时检测(仅限内网访问)
示例关键逻辑:
var healthStatus atomic.Value // 存 map[string]bool
healthStatus.Store(map[string]bool{"db": true, "redis": true})
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
status := map[string]bool{
"db": pingDB(),
"redis": pingRedis(),
}
healthStatus.Store(status)
}
}()
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("verbose") == "1" {
// 调试用,跳过缓存,实时探测(建议加 IP 白名单)
...
return
}
statuses := healthStatus.Load().(map[string]bool)
for _, ok := range statuses {
if !ok {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("dependency failed"))
return
}
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
K8s livenessProbe 和 readinessProbe 怎么配
两者必须分开,不能都指向 /health:
-
livenessProbe:只检查进程是否卡死(比如 goroutine 泄漏、死锁),建议用极简版(不查依赖),失败后容器重启 -
readinessProbe:检查服务是否准备好接收流量,可包含依赖检测,失败后从 service endpoint 中摘除,但不重启
典型 YAML 片段:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
注意:/readyz 应该比 /health 更严格,且它的响应时间必须稳定——如果某次 Redis 探测慢了 3 秒,K8s 就会把它从 endpoints 列表里踢出去,即使服务本身完全正常。










