当服务在一个数据中心内每个请求要互相调用上千次时,JSON-over-HTTP/1.1 的冗长就成了真实的税。gRPC 是 Google 的答案:一个高性能、契约优先的 RPC 框架,构建在 Protocol Buffers(一种紧凑的二进制格式)和 HTTP/2 之上。它是 API 三件套里 REST、GraphQL 之外的第三位——它发光的地方正是它们不擅长的:低延迟、高吞吐、跨语言的服务间通信。它直接建立在我们 DDIA 编码与演化笔记 的编码思想上,并与我们的 GraphQL vs REST 对比互补。

⚡ 速览要点
  • 契约优先的 RPC——你在一个 .proto 文件里定义服务和消息,生成多种语言的强类型客户端 + 服务端 stub。
  • Protocol Buffers——紧凑、带 schema 的二进制格式;比 JSON 小得多、解析快得多,内建 schema 演化。
  • 构建在 HTTP/2 上——多路复用、二进制分帧、头部压缩;流式的基础。
  • 四种调用——一元(unary)、服务端流、客户端流、双向流。
  • 最适合内部微服务——低延迟、高吞吐、严格契约、多语言团队。
  • 对公开/浏览器 API 较弱——不可人读,且浏览器需要 grpc-web 代理;REST/GraphQL 在公开边缘仍占优。
tldr

gRPC 是契约优先的 RPC 框架:在 .proto 里定义 API,生成类型化 stub,像调本地函数一样调远程方法。它用 Protocol Buffers(紧凑二进制)在 HTTP/2 上序列化,带来低延迟、小载荷和四种流模式。它非常适合内部、性能敏感、多语言的服务间通信,而不适合公开 API 或浏览器直连(那里人读的 JSON 和 REST/GraphQL 仍胜)。

gRPC 是什么

gRPC 是一个 RPC(远程过程调用) 框架:目标是让调用远程服务的方法感觉像调本地函数。它是契约优先的——你先写一个接口定义(.proto)描述服务的方法和消息类型,然后编译器为你的语言生成客户端和服务端 stub。客户端调一个生成的方法;stub 处理序列化、网络和反序列化。(DDIA 里那个经典告诫仍成立:远程调用其实不像本地调用——它可能失败或超时——所以要为部分失败而设计。)

Protocol Buffers

载荷格式是 Protocol Buffers(protobuf)——一种基于 schema 的二进制编码。你声明带编号字段的类型化消息;编码后的字节携带字段标签(tag)而非名字,使其紧凑、解析快,并有明确的 schema 演化规则(用新 tag 加字段,绝不复用 tag)。

user.proto — 契约
service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListReq) returns (stream User);  // 服务端流
}
message User {
  string id    = 1;
  string name  = 2;
  int64  since = 3;
}

从这一个文件,protoc 生成 Go、Java、Python 等的类型化客户端和服务端——契约是单一事实来源,两端保证在线格式一致。相比 JSON,protobuf 明显更小、编解码更快,这是 gRPC 性能优势的大头。

构建在 HTTP/2 上

gRPC 跑在 HTTP/2 上,这个选择解锁了 protobuf 之外的大部分能力:

四种调用类型

因为 HTTP/2 双向支持流,gRPC 提供四种方法形态——相比请求/响应式 REST 是个真实优势:

四种 RPC 模式
一元(unary)     客户端 → 1 请求    | 1 响应 ← 服务端     (普通调用)
服务端流          客户端 → 1 请求    | 多个响应 ← 服务端   (feed/结果集)
客户端流          客户端 → 多个请求  | 1 响应 ← 服务端     (上传/聚合)
双向流            客户端 ↔ 多个      | 多个 ↔ 服务端       (聊天、实时)

一元是日常的请求/响应。流模式在大结果集(服务端流)、上传/遥测(客户端流)、交互式低延迟交换(双向流)上发光——这些在普通 REST 上很别扭。

代码生成与契约优先开发

这个工作流把 REST 常见的"先写端点再写文档"习惯反了过来。用 gRPC,.proto 就是 API:你先设计它,为每个服务生成 stub,编译器强制调用方和实现方一致。好处是跨语言的类型安全(一个 Go 服务和一个 Python 客户端共享一份契约)、无需手写序列化、schema 兼作活文档。代价是一个构建步骤和一份要版本化分发的共享 .proto

gRPC 对比 REST 对比 GraphQL

方面gRPCRESTGraphQL
载荷Protobuf(二进制)JSON(文本)JSON(文本)
传输HTTP/2HTTP/1.1 或 2HTTP
契约严格(.proto)松散(OpenAPI 可选)严格(schema)
流式一等公民(4 种)无(单请求/响应)订阅
浏览器需 grpc-web 代理原生原生
最适合内部微服务公开/简单 API灵活的客户端查询

何时用 gRPC(以及何时不用)

用 gRPC 做内部、性能敏感的服务间通信——尤其在多语言微服务系统里需要严格契约和小而快的载荷,或者需要流式时。它是 API 网关 后面东西向流量的常见选择,网关常把外部 REST 转换成内部 gRPC。别用它做公开 API(消费者期望可读 JSON、好 curl)和浏览器直连(浏览器不能讲裸 gRPC,需要 grpc-web 代理)。常见架构:公开边缘用 REST/GraphQL,内部服务间用 gRPC。

局限与常见坑

总结

gRPC = Protocol Buffers(紧凑二进制、带 schema、可演化)+ HTTP/2(多路复用、流式)+ 契约优先的代码生成。这个组合让它快、跨语言强类型,非常适合内部微服务流量和流式——而它二进制、非浏览器原生的本性让 REST/GraphQL 在公开和前端面向的 API 上保持领先。务实的模式是:边缘 REST,内部 gRPC。

🎯 面试速答

什么是 gRPC?用 Protocol Buffers 跑在 HTTP/2 上的契约优先 RPC 框架,生成类型化 stub,让远程调用看起来像本地方法。
为什么比 REST/JSON 快?protobuf 是紧凑二进制(用 tag 而非字段名)、解析快,HTTP/2 在一条连接上多路复用许多调用并压缩头部。
四种调用类型?一元、服务端流、客户端流、双向流——由 HTTP/2 的流实现。
什么时候不该用 gRPC?公开 API(想要可读 JSON)和浏览器直连(需要 grpc-web 代理);那里用 REST/GraphQL。
它和 protobuf schema 演化的关系?用新 tag 号加字段,绝不复用或改 tag——从而向后/向前兼容(见编码与演化)。

← 上一篇
API 网关与服务网格