Go HTTP服务健康检查需分离/readyz与/livez端点:/readyz检查DB等依赖,/livez仅检测进程僵死;并发探测依赖并设1.5秒超时;Shutdown前须关闭健康端点或用原子开关。

Go HTTP 服务内置 /health 端点怎么写才不踩坑
直接暴露 http.HandleFunc("/health", ...) 很容易返回 200 却掩盖真实问题。健康检查必须区分「服务可访问」和「服务可工作」——比如数据库连不上时,/health 仍返回 200 就会误导 Kubernetes 的 livenessProbe。
- 用
http.HandlerFunc而非中间件封装,避免被其他中间件(如 auth、logging)干扰状态码 - 响应体必须是纯文本或极简 JSON,不要带额外 header(如
X-Request-ID),K8s 默认只读取状态码和 body 前几 KB - 超时必须硬性控制:整个 handler 执行不能超过 2 秒,否则 K8s 会触发重启;建议用
context.WithTimeout(r.Context(), 1500*time.Millisecond) - 不要在健康检查里调用外部服务的「完整链路」,只查直连依赖:DB 连接、本地磁盘空间、关键 goroutine 是否卡死
net/http 中如何做依赖探测而不阻塞主线程
健康检查里逐个 ping MySQL、Redis、Etcd 容易因某个依赖超时拖垮整个 endpoint。正确做法是并发探测 + 快速失败。
func healthHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 1500*time.Millisecond)
defer cancel()
type result struct {
name string
err error
}
ch := make(chan result, 3)
go func() {
err := checkMySQL(ctx)
ch zuojiankuohaophpcn- result{name: "mysql", err: err}
}()
go func() {
err := checkRedis(ctx)
ch zuojiankuohaophpcn- result{name: "redis", err: err}
}()
go func() {
err := checkDisk(ctx)
ch zuojiankuohaophpcn- result{name: "disk", err: err}
}()
var failed []string
for i := 0; i zuojiankuohaophpcn 3; i++ {
select {
case r := zuojiankuohaophpcn-ch:
if r.err != nil {
failed = append(failed, r.name)
}
case zuojiankuohaophpcn-ctx.Done():
http.Error(w, "timeout", http.StatusServiceUnavailable)
return
}
}
if len(failed) > 0 {
w.WriteHeader(http.StatusServiceUnavailable)
fmt.Fprintf(w, "unhealthy: %v", strings.Join(failed, ", "))
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")}
Kubernetes readinessProbe 和 livenessProbe 怎么配 Go 服务
两个 probe 语义完全不同:readinessProbe 决定是否往 Pod 转发流量,livenessProbe 决定是否杀掉容器重拉。用同一个 /health endpoint 是常见错误。
立即学习“go语言免费学习笔记(深入)”;
-
readinessProbe应该调用/readyz,检查 DB 连接、配置加载、gRPC server 是否已启动监听 -
livenessProbe应该调用/livez,只检查进程是否僵死(例如通过runtime.NumGoroutine()判断是否远超正常值,或检测http.Server.Shutdown是否卡住) - 两者都必须设
initialDelaySeconds: 10,防止容器刚启动就探活失败被误杀 - 不要用
exec探针调curl,Go 二进制默认不带 shell,要用httpGet直接请求
为什么 http.Server.Shutdown() 期间健康检查会失效
当调用 srv.Shutdown() 后,http.Serve() 返回,但 handler 函数仍可能在执行中。如果此时 K8s 发来健康检查请求,会 panic 或返回空响应。
- 必须在
Shutdown前先关闭健康检查 endpoint:用http.NewServeMux()分离主路由和健康路由,shutdown 时只关主 mux - 或者用原子开关:定义
var isShuttingDown atomic.Bool,所有健康 handler 开头加if isShuttingDown.Load() { w.WriteHeader(http.StatusServiceUnavailable); return } - 更稳妥的是用
server.RegisterOnShutdown()回调,在 shutdown 开始时主动把/health状态置为 failing
实际部署中最容易被忽略的是 probe 的 failureThreshold 和 periodSeconds 组合。比如设成 failureThreshold: 1 + periodSeconds: 1,只要一次网络抖动就杀 Pod,比健康检查逻辑本身还危险。










