Golang服务健康检查应暴露轻量探活端点,HTTP适用于多数场景(如K8s、Nginx),gRPC适合内部强契约通信;需避免耗时操作、引入状态缓存、分离Liveness/Readiness逻辑。

用 Golang 做服务健康检查,核心是暴露一个轻量、可靠、低开销的探活端点,并让外部(如 K8s、Consul、Nginx 或自建巡检系统)能定期调用它。HTTP 和 gRPC 各有适用场景:HTTP 简单通用,适合大多数基础设施集成;gRPC 更适合内部微服务间强契约、低延迟的健康通信,但需客户端支持。
HTTP 健康检查:简单可靠,推荐默认方案
HTTP 方式最常用,Kubernetes Liveness/Readiness、Nginx upstream health check、Prometheus blackbox exporter 都原生支持。关键是要返回明确状态码 + 可读响应体。
- 用标准 http.HandleFunc 注册
/healthz(或/health)端点,返回 200 OK 表示存活,非 200(如 503)表示异常 - 避免在健康端点中做耗时操作(如 DB 连接池 ping、远程 HTTP 调用),只检查本地关键依赖是否就绪(例如:DB 连接池是否非空、Redis client 是否可 Ping、配置是否加载完成)
- 可返回 JSON 响应体辅助排障,例如:
{"status":"ok","checks":{"db":"ok","redis":"ok"}},但不要影响状态码逻辑 - 建议加 Timeout 和 Keep-Alive 控制:启动 HTTP server 时设置
ReadTimeout、WriteTimeout,防止健康接口被阻塞拖垮整个服务
gRPC 健康检查:符合 gRPC 生态,适合服务网格场景
如果你的服务已全面采用 gRPC,且调用方也是 gRPC 客户端(如 Istio、gRPC-Go 客户端),可用官方 gRPC Health Checking Protocol,它定义了标准的 HealthCheckService 接口。
- 导入
google.golang.org/grpc/health和google.golang.org/grpc/health/grpc_health_v1 - 创建
health.Server实例,调用SetServingStatus主动上报服务状态(servicename为空字符串表示默认服务) - 将 health server 注册到你的 gRPC server:
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer) - 客户端用
grpc_health_v1.NewHealthClient(conn).Check(ctx, &grpc_health_v1.HealthCheckRequest{Service: ""})发起探活,成功且status == SERVING即为健康 - 注意:gRPC 健康检查本身不自动探测后端依赖,仍需你在
SetServingStatus前同步执行轻量检查(如检查数据库连接是否有效)
主动定时自检 + 状态缓存:避免每次探活都触发检查
频繁的探活请求(尤其高并发时)若每次都实时检查 DB/Redis,会带来不必要压力。更优做法是后台 goroutine 定期执行真实检查,把结果缓存在内存中,探活端点只读取快照。
立即学习“go语言免费学习笔记(深入)”;
- 启动时起一个 goroutine,用
time.Ticker每 5–10 秒执行一次依赖检查(如db.PingContext()) - 用 atomic.Value 或 sync.RWMutex 包裹状态结构体(如
type HealthStatus struct { DBOK, RedisOK bool }),保证并发安全读写 - HTTP/gRPC 探活 handler 中只做快速读取,根据缓存状态决定返回码和响应体
- 这样既保证探活低延迟,又避免雪崩式下游探测压垮依赖服务
集成 Kubernetes:Liveness 与 Readiness 分离设计
K8s 中 livenessProbe 决定是否重启容器,readinessProbe 决定是否加入 Service Endpoint。二者逻辑应不同:
- Liveness:只检查进程是否卡死(如 goroutine 泄漏、死锁),可仅检测 HTTP 端口是否可连、gRPC server 是否 accept,**不检查外部依赖**(否则 DB 挂了会导致无限重启)
- Readiness:检查服务是否真正可服务,包括关键依赖(DB、Redis、配置中心)。只有全部 OK 才返回 200 / SERVING,否则 503 / NOT_SERVING,让流量暂时绕过
- Golang 中可复用同一套检查逻辑,但通过 URL path 或 gRPC service name 区分(如
/healthz/livenessvs/healthz/readiness)
基本上就这些。HTTP 适合绝大多数场景,开箱即用;gRPC 健康检查适合深度 gRPC 架构;加上状态缓存和 K8s 探针分离,就能支撑生产级稳定性要求。不复杂但容易忽略细节——关键是别让健康检查本身成为故障源。










