gRPC健康检查协议需手动注册health.Server并调用healthpb.RegisterHealthServer,否则客户端调用Check会返回UNIMPLEMENTED;服务端须用SetServingStatus动态更新状态,客户端需设短超时并避免复用失效连接。

gRPC 健康检查协议不是默认开启的,必须手动注册服务
Go 的 grpc-go 官方库本身不自动提供健康检查服务;即使你启用了 grpc.HealthCheck 相关 import,也得显式注册 health.NewServer() 实例并绑定到 gRPC 服务端。漏掉这一步,客户端调用 /grpc.health.v1.Health/Check 就会直接报 UNIMPLEMENTED。
常见错误现象:用 grpcurl 执行 grpcurl -plaintext localhost:8080 grpc.health.v1.Health/Check 返回 rpc error: code = Unimplemented desc = method not implemented——大概率就是没注册。
- 必须在
grpc.Server启动前,调用grpc.RegisterService(server, &healthServer)(旧版)或更推荐的healthpb.RegisterHealthServer(server, healthServer)(v1.33+) - 健康服务实例建议用
health.NewServer()初始化,它内部已支持状态自动更新和ServingStatus管理 - 不要自己实现
Check方法去硬返回status.OK——那样无法反映真实服务状态,也绕过了健康检查协议语义
如何动态更新服务健康状态(比如依赖 DB 连接断开时置为 NOT_SERVING)
health.Server 提供了 SetServingStatus 方法,这是控制健康状态的核心接口。它的第一个参数是服务名(空字符串 "" 表示整个服务),第二个是 healthpb.HealthCheckResponse_ServingStatus 枚举值。
典型使用场景:服务启动后尝试连接 PostgreSQL,连接失败就将全局状态设为 NOT_SERVING,等重连成功再切回 SERVING。
- 服务名传
""即可控制整体健康状态;传具体服务名(如"myservice.UserService")可用于细粒度控制(需客户端指定服务名调用Check) - 状态变更不是原子广播,但对大多数客户端足够及时;不需要加锁,
health.Server内部已做同步 - 别在 HTTP 中间件里反复调用
SetServingStatus—— 频繁写入可能引发竞争,应由独立 goroutine + 退避重试机制驱动状态更新
客户端调用 Check 时常见的超时与错误处理陷阱
gRPC 健康检查本质仍是 RPC 调用,因此受 gRPC 超时、取消、连接状态影响。很多用户误以为 Check 是轻量探测,结果在服务卡住时阻塞数秒才返回。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
错误现象:Kubernetes 的 livenessProbe 配置了 timeoutSeconds: 1,但 gRPC 健康检查因底层 TCP 握手慢或服务端卡顿,实际耗时 5s+,导致容器被反复 kill。
- 务必为健康检查客户端设置合理的
grpc.WaitForReady(false)和短超时(如ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)) - 避免复用长生命周期的
grpc.ClientConn实例做健康检查——连接可能已断开但未及时感知;建议每次检查新建轻量 conn 或复用带健康监测的 conn -
NOT_FOUND错误通常表示服务端根本没注册健康服务;UNAVAILABLE多为网络层问题;只有OK和明确返回NOT_SERVING才算业务层健康反馈
与 Kubernetes liveness/readiness probe 集成时要注意路径和协议匹配
Kubernetes 默认的 exec 或 httpGet 探针无法直接调用 gRPC 方法。必须用 grpc 类型探针(v1.23+ 支持),或借助 sidecar(如 grpc-health-probe)桥接。
常见配置错误:在 readinessProbe 里写 httpGet: path: /healthz,但你的服务根本没有暴露 HTTP 端点——gRPC 健康检查不走 HTTP 路由。
- 若用
kubectl wait --for=condition=ready pod/xxx,它依赖的是 Pod 的containerStatuses.ready字段,该字段由探针结果驱动,和 gRPC 健康状态无直接关系 - 推荐方案:在容器中运行
grpc-health-probe -addr=:8080 -connect-timeout=1s -rpc-timeout=2s,它会主动调用Check并按返回码退出(0=healthy) - 注意
grpc-health-probe默认检测全局状态(service=""),如需检测子服务,得加-service=myapi.v1.UserService
真正麻烦的不是注册健康服务,而是让状态变更与后端依赖(DB、Redis、下游 gRPC 服务)的实际可用性严格对齐——这里没有银弹,得结合具体依赖的失败特征设计探测逻辑和恢复策略。

















