必须分离/healthz和/livez端点:/livez仅检查进程存活(端口监听、goroutine数、内存),超时≤100ms;/healthz检查依赖就绪(DB、Redis等),带独立超时控制,避免抖动引发误重启。

直接暴露 /healthz 和 /livez 两个分离的 HTTP 端点,是当前 Kubernetes + Go 生产环境最稳妥、最不易翻车的做法。只用一个端点、或依赖 exec 探针,大概率会在依赖抖动时触发误重启。
为什么不能只写一个 /health 端点
常见错误是把数据库连通性、Redis Ping、下游 HTTP 调用全塞进同一个 handler。Kubernetes 的 livenessProbe 一旦失败就会杀容器——而 DB 临时超时、网络抖动、中间件维护都是常态。结果就是 Pod 反复拉起又杀死,形成雪崩循环。
-
/livez必须只检查进程级存活:HTTP server 是否还在 accept 连接、关键 goroutine 是否卡死(比如用runtime.NumGoroutine()做阈值判断)、内存是否暴涨(runtime.ReadMemStats) -
/healthz才负责依赖链:DBPingContext、RedisPing、关键 gRPC clientConnect,每个都带独立context.WithTimeout - 两个端点响应时间必须严格控制:
/livez应 /healthz 建议 ≤2s;否则 K8s 会因超时反复重试
Go 代码里怎么安全实现 /livez 和 /healthz
别在 handler 里每次请求都真实探测依赖——Kubernetes 默认每 10 秒调一次,高频探测会压垮下游。正确做法是后台定时巡检 + 缓存状态 + 原子读取。
- 用
time.Ticker启动后台 goroutine,每 5 秒执行一次 DB/Redis 检查,结果写入atomic.Bool或sync.RWMutex保护的结构体 -
/livezhandler 只做三件事:检查http.Server是否还在监听、runtime.NumGoroutine()是否 -
/healthzhandler 读缓存状态,不重新发起网络请求;若某依赖缓存为 false,立即返回 503 并带上字段如{"db": "unavailable"} - 收到
SIGTERM时,先atomic.StoreBool(&isReady, false),再停Ticker,最后关闭http.Server
Kubernetes YAML 中 livenessProbe 和 readinessProbe 怎么配才不打架
两个探针共用同一 handler 是线上高频事故源。它们语义不同,参数也必须差异化配置。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
livenessProbe:指向/livez,initialDelaySeconds: 30(给 Go 初始化留足时间),periodSeconds: 10,failureThreshold: 3(连续三次失败才重启) -
readinessProbe:指向/healthz,initialDelaySeconds: 15(比 liveness 短,但必须 > 服务实际就绪耗时),timeoutSeconds: 2(必须 ≥ handler 内部超时设置) - 绝对禁止
httpGet不设timeoutSeconds:默认是 1 秒,很多 DB Ping 在高负载下会超时,直接导致 readiness 失败、流量被摘 - 不要给
readinessProbe设太小的periodSeconds:比如 2 秒,短时间抖动会频繁震荡 Endpoint 列表,下游 DNS 缓存可能来不及刷新
Gin 框架下健康端点容易踩的坑
用 Gin 写 /healthz 很方便,但默认行为会埋雷。router.Run()、DebugMode、无超时 http.Server 是三大高危操作。
- 启动前必须显式设
gin.SetMode(gin.ReleaseMode),否则/healthz返回 500 时会暴露完整堆栈,泄露路径和版本信息 - 别用
router.Run(),改用http.Server实例:设ReadTimeout: 30s、WriteTimeout: 30s、IdleTimeout: 60s(匹配 Nginx keepalive) - 健康 handler 必须注册为
router.GET("/healthz", healthHandler),不要走中间件链——中间件可能加日志、鉴权、trace,拖慢响应甚至吞掉错误 - 如果用了 Gin 的
Recovery中间件,要确保它不捕获健康端点的 panic;否则/healthzpanic 后仍返回 200,K8s 认为一切正常
最常被忽略的是状态缓存的生命周期管理:后台 ticker 和主服务 shutdown 信号必须用同一个 context.Context 控制,否则热更新时旧 goroutine 继续写状态,新 handler 读到脏数据,健康接口就彻底失去意义。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










