健康检查接口必须返回标准HTTP状态码:/healthz校验下游依赖失败时返回503,/livez仅确认进程存活返回200;pprof需鉴权访问;推荐用OpenTelemetry healthcheck替代手写逻辑;K8s探针配置须匹配服务实际行为。
Go 服务的健康检查不是“能访问就行”,客户端(如 Kubernetes、Nginx、Consul)依赖 HTTP 200 表示“就绪且可流量”,HTTP 503 表示“暂时不可用”。返回 200 但 body 写 {"status":"down"} 没用——多数探针只看状态码,不解析 JSON。
实操建议:
http.HandleFunc 或 chi.Router 注册 /healthz(就绪)和 /livez(存活),避免混用/healthz)应校验下游依赖:数据库连接、Redis 可写、关键 gRPC 服务连通性;失败则返回 http.StatusServiceUnavailable (503)
/livez)只确认进程未卡死:不查外部依赖,仅 return http.StatusOK
net/http/pprof 前必须限制访问来源net/http/pprof 提供 /debug/pprof/ 下的运行时指标(goroutine、heap、trace),是健康监控的事实标准,但它本身不是健康检查接口,且暴露后有安全风险。
常见错误现象:
http.Handle("/debug/pprof/", http.DefaultServeMux) → 任意公网 IP 都能 dump 堆栈或 CPU profilepprof 但没加中间件鉴权 → 被扫描工具批量抓取,拖慢服务实操建议:
debug 构建标签*册:if os.Getenv("DEBUG") == "true" {
mux := http.NewServeMux()
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
http.ListenAndServe(":6060", mux)
}func pprofAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
if 
!slices.Contains([]string{"127.0.0.1", "10.0.0.0"}, ip) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}go.opentelemetry.io/otel/healthcheck 替代手写逻辑手动拼接 /healthz 的 JSON、管理依赖状态、加锁防并发竞争,容易出错。OpenTelemetry 官方维护的 healthcheck 包提供可组合、可观察的健康检查抽象。
使用场景:
otel_health_check_up{component="postgres"} 1)参数差异与注意点:
healthcheck.NewChecker() 默认超时是 3s,可通过 WithTimeout(5 * time.Second) 调整healthcheck.CheckerFunc,返回 error 即表示失败(不要自己转成字符串)hc := healthcheck.NewChecker()
hc.Add("postgres", healthcheck.CheckerFunc(func(ctx context.Context) error {
return db.PingContext(ctx)
}))
http.HandleFunc("/healthz", hc.Handler())很多团队把 livenessProbe 设成 5 秒超时、3 次失败就重启,结果因一次 DB 临时抖动(持续 8 秒),Pod 被反复 kill/restart,雪崩式影响更大。
关键判断依据:
readinessProbe 失败 → 从 Service Endpoints 移除,不再接收新流量;适合配短周期(periodSeconds: 5)、低失败阈值(failureThreshold: 2)livenessProbe 失败 → 触发容器重启;必须比业务最长处理时间长,且只用于检测“进程假死”(如 goroutine 泄漏卡住 HTTP server),不是网络抖动兜底方案http.Server.ReadTimeout 是 0(无限制),若健康接口阻塞,K8s 探针会等满 timeoutSeconds 才判定失败,期间所有请求 hang 住实操建议:
http.Server 中显式设 ReadTimeout: 30 * time.Second,避免单个慢请求拖垮整个探针/livez 单独起一个轻量 http.Server(监听 :8081),完全不走主路由中间件,确保即使主服务卡死也能响应存活检查log.Printf("healthz called, elapsed: %v", time.Since(start)),便于定位是代码慢还是依赖慢健康检查不是加个路由就完事,真正难的是定义“什么算健康”——数据库连得上但慢十倍,算健康吗?消息队列积压 10 万条,算健康吗?这些边界必须结合业务 SLA 明确,然后才轮到 Go 怎么写。