gRPC 健康检查协议
protobuf syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest { string service = 1; }
message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; } ServingStatus status = 1; }
service Health { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); }
#### 两个核心方法
##### Check(单次检查)
客户端发送一个 `HealthCheckRequest`,其中 `service` 字段指定要检查的服务名称。如果服务名称为空字符串,则表示检查**整个 gRPC 服务器**的整体健康状态。服务器返回当前瞬间的状态。
##### Watch(流式监视)
与 `Check` 使用相同的请求参数,但响应是一个**服务器端流**。一旦调用,服务器会持续发送健康状态更新(例如状态从 `SERVING` 变为 `NOT_SERVING` 时立即推送)。这种方法避免了客户端轮询,对负载均衡器和边车代理特别有用。
### ServingStatus 状态码详解
服务端返回的状态包含三种可能,理解它们对正确配置探针至关重要:
- **`UNKNOWN (0)`**:服务状态未知。这通常出现在服务启动初期,尚未完成初始化,或者客户端查询了一个服务器不认识的 `service` 名称。负载均衡器应当将其视为不可用。
- **`SERVING (1)`**:服务正常,可以接受请求。对于空 `service` 参数,表示整个服务器健康;对于特定服务,则表示该已注册的服务就绪。
- **`NOT_SERVING (2)`**:服务明确表示不能处理请求。可能因为正在进行优雅关闭、依赖的下游服务不可用、或服务自身出现不可恢复的错误。收到此状态后,客户端应停止将请求路由至此实例。
### 服务端实现指引
所有主流 gRPC 语言实现都提供了健康检查协议的官方或半官方库,开发者只需创建健康服务实例并注册到现有 gRPC 服务器即可。下面以 Go 和 Java 为例展示典型写法。
#### Go 语言实现
```go
import (
"google.golang.org/grpc"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
func main() {
s := grpc.NewServer()
// 创建健康服务
hs := health.NewServer()
// 注册健康服务到 gRPC 服务器
healthpb.RegisterHealthServer(s, hs)
// 对于服务名为空(整体服务器状态),设置为 SERVING
hs.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
// 对于特定服务 "myapp.EchoService" 设置状态
hs.SetServingStatus("myapp.EchoService", healthpb.HealthCheckResponse_SERVING)
// 监听并启动...
}
health.NewServer() 返回的对象提供 SetServingStatus(service string, status healthpb.HealthCheckResponse_ServingStatus) 方法,可在服务生命周期的不同阶段动态调用(例如初始化完成后设为 SERVING,开始优雅关闭时设为 NOT_SERVING)。
Java 语言实现
import io.grpc.protobuf.services.HealthStatusManager;
import io.grpc.services.HealthStatusManager.ServingStatus;
import io.grpc.services.HealthCheck;
public class MyGrpcServer {
public static void main(String[] args) {
Server server = ServerBuilder.forPort(50051)
.addService(new MyServiceImpl())
.build();
HealthStatusManager health = new HealthStatusManager();
health.setStatus("", ServingStatus.SERVING);
health.setStatus("myapp.EchoService", ServingStatus.SERVING);
server.getServices().add(health.getHealthService());
server.start();
// ...
}
}
Java 的 HealthStatusManager 使用方式类似,通过 setStatus 更新状态。一些高级框架(如 Spring Boot + gRPC)会自动集成健康检查服务,开发者只需配置即可。
客户端使用与集成场景
作为普通 gRPC 客户端调用
任何 gRPC 客户端都可以直接调用健康检查服务来探测目标服务器。例如,在服务发现时,先执行一次 Check,只将 SERVING 状态的实例加入可用列表。或者使用 Watch 流长期跟踪状态变化,及时摘除不健康的实例。
Kubernetes gRPC 探针
从 Kubernetes 1.24 开始,Pod 的存活和就绪探针原生支持 gRPC。无需额外使用 exec 脚本或 HTTP 端口,只需在 Pod 定义中指定:
apiVersion: v1
kind: Pod
metadata:
name: my-grpc-app
spec:
containers:
- name: server
image: my-grpc-image
ports:
- containerPort: 50051
startupProbe:
grpc:
port: 50051
failureThreshold: 30
periodSeconds: 10
livenessProbe:
grpc:
port: 50051
readinessProbe:
grpc:
port: 50051
initialDelaySeconds: 5