gRPC 是一种高性能远程过程调用框架,让一个程序像调用本地方法一样调用另一台机器上的服务方法,并用 schema 生成跨语言客户端和服务端代码。
核心问题
分布式系统里的服务通常运行在不同进程、机器或语言环境中。调用方希望表达的是“调用 SayHello(name) 得到 HelloReply”,但网络上实际只能传输字节、连接状态和错误码。gRPC 解决的是把远程服务抽象成类型化方法,同时把编码、连接、状态码、超时和流控收敛到统一框架里。
核心对象
| 对象 | 作用 | 工程关注点 |
|---|---|---|
.proto 文件 | 定义消息类型和 service 方法 | 字段编号、兼容性、包名 |
| Message | 请求或响应的数据结构 | 类型、字段编号、默认值、未知字段 |
| Service | 一组可远程调用的方法 | 方法名、输入类型、输出类型 |
| Client stub | 客户端本地代理对象 | timeout、metadata、重试、连接复用 |
| Server implementation | 服务端真实处理逻辑 | 并发、鉴权、错误映射 |
| Channel | 客户端到服务端的逻辑连接 | 连接池、TLS、负载均衡 |
| Status | RPC 调用结果 | OK、超时、不可用、权限错误 |
核心机制
一个最小 gRPC service 通常写成:
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}机制可以拆成四步:
- 开发者在
.proto中定义消息和 service。 protoc和 gRPC 插件生成客户端 stub、服务端接口和 Protocol Buffers 编解码代码。- 客户端调用本地 stub 方法;stub 把请求对象序列化成 protobuf 字节,并通过基于 HTTP/2 的传输发送。
- 服务端收到请求后反序列化,分发到实现方法,返回响应消息和 status。
这里“像本地调用”只是编程模型,不是运行时事实。真实调用仍然跨网络,会遇到超时、连接断开、重试导致的重复请求、服务端部分失败和版本兼容问题。
gRPC 支持多种调用形态:
- Unary RPC:一个请求对应一个响应,OTLP/gRPC 的 export 请求就属于这个直觉。
- Server streaming:一个请求对应多个响应。
- Client streaming:多个请求汇成一个响应。
- Bidirectional streaming:双方都可以持续发送消息。
工程用途
- 微服务之间的高吞吐、低延迟内部 API。
- 多语言服务接口,借助生成代码减少手写客户端。
- 强 schema 的控制面或数据面协议。
- 遥测、配置、网关和 Agent 之间的二进制传输,例如 OTLP/gRPC。
观察 gRPC 系统时,重点看请求延迟、status code 分布、deadline exceeded、unavailable、重试次数、连接数、消息大小和服务端并发。
边界与常见坑
- gRPC 不是 REST:它用 service/method 建模,不以资源 URL 和 HTTP verb 作为核心抽象。
- 本地调用只是表象:远程调用必须显式设计 timeout、幂等、重试和错误处理。
- proto 字段编号不能随便改:字段名可以生成不同语言代码,但 wire format 依赖字段编号。
- 浏览器支持需要额外考虑:传统 gRPC 基于 HTTP/2 语义,浏览器场景常需要 gRPC-Web 或网关。
- 流式能力不等于无限缓冲:streaming 仍受流控、队列、内存和服务端处理速度限制。
- 认证不应只靠内网假设:生产环境需要 TLS、mTLS、token 或网关策略来建立调用方身份。