Golang健康检查接口如何设计_探针接口实现说明

必须分开暴露/healthz和/readyz端点:/healthz仅检查进程存活(端口、goroutine、时钟),要求快稳无副作用;/readyz检查服务就绪(DB、Redis等依赖),需异步预热+超时控制;两者均须返回200或503,且探针参数须与代码耗时对齐。

必须分开暴露 /healthz/readyz 两个端点,不能复用同一接口——这是 K8s 健康管理的语义底线。

为什么不能只写一个 /health

因为 livenessreadiness 在 Kubernetes 中触发的动作完全不同:前者失败会杀容器重启,后者失败只是摘流量。如果共用一个接口,只要数据库挂了,liveness 就会误判并反复重启健康进程,形成“假死循环”。

  • /healthz 只检查进程是否活着:监听端口是否可用、主 goroutine 是否卡死、系统时钟是否异常
  • /readyz 检查服务是否真能干活:DB 连接池是否 ready、Redis 是否可 ping、配置是否加载完毕、gRPC 客户端是否处于 READY 状态
  • 两者都必须返回标准 HTTP 状态码:200 OK 表示通过,503 Service Unavailable 表示失败(K8s 默认只认这个)

/healthz 怎么写才安全?

核心原则是「快、稳、无副作用」:不能调外部依赖,不能写日志,不能做任何可能阻塞或超时的操作。它不是诊断工具,而是生死开关。

  • 避免在 handler 里调 db.Ping()redis.Ping() —— 网络抖动会导致误杀
  • 可以检查 time.Since(startTime) (防时钟漂移)、runtime.NumGoroutine() > 10000(防 goroutine 泄漏)
  • 建议加一个轻量心跳标记,比如启动时启动一个 goroutine 定期更新 atomic.Bool/healthz 只读它
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    if time.Since(startTime

) < 0 { http.Error(w, "clock skew", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) })

/readyz 怎么避免拖慢请求或误判?

就绪检查天然比存活检查重,但依然不能阻塞主线程。常见错误是每次请求都实时连 DB,结果高并发下探针本身变成性能瓶颈。

  • 所有依赖检查必须带 context.WithTimeout,超时建议 ≤ 2s
  • 用异步预热 + 状态缓存:启动时开 goroutine 轮询 DB/Redis,把结果存在 atomic.Boolsync.Map
  • 不要在 /readyz 里执行 SELECT 1,用连接池的 Ping()Stats().OpenConnections 更轻量
  • 若依赖多个组件,任一失败就立即返回 503,不要等全部检查完
var dbReady = atomic.Bool{}
go func() {
    for i := 0; i < 3; i++ {
        if err := db.PingContext(context.Background(), 2*time.Second); err == nil {
            dbReady.Store(true)
            return
        }
        time.Sleep(1 * time.Second)
    }
}()

http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) { if !dbReady.Load() { http.Error(w, "db not ready", http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK) w.Write([]byte("ready")) })

K8s 探针配置参数怎么设才不翻车?

参数不是随便填的,要和你代码里的耗时、服务启动时间对齐。最常踩的坑是 initialDelaySeconds 设太小,导致容器还没初始化完就被 liveness 杀掉。

  • livenessProbeinitialDelaySeconds: 15(给服务足够启动+预热时间),timeoutSeconds: 3(匹配代码中所有超时设置)
  • readinessProbeinitialDelaySeconds: 5(早于 liveness 启动,让流量晚点进来),periodSeconds: 5(高频探测,快速摘除异常实例)
  • 启动慢的服务(如加载大模型、预热缓存),必须加 startupProbe,否则 liveness 会抢在它活过来之前把它干掉

最容易被忽略的一点:所有健康检查逻辑必须在服务 启动阶段就注册好,而不是等某个 init 函数跑完才挂路由——否则探针在初始化期间看到的是 404,直接判死。