gRPC 是什么?它相比 REST/HTTP 有哪些优势?
简化版
gRPC 是 Google 开源的高性能 RPC 框架——让你像调用本地方法一样调用远程服务。它有两个技术底座:① 传输用 HTTP/2(多路复用、头部压缩、支持双向流);② 数据用 Protobuf(二进制序列化,比 JSON 更小更快,还靠 .proto 文件自动生成各语言代码)。相比 REST/JSON over HTTP/1.1,gRPC 更快(二进制 + HTTP/2)、更省带宽、有强类型契约、原生支持四种流式调用,特别适合内部微服务之间高频通信;缺点是浏览器不能直接用、可读性差、调试不如 JSON 直观。
详细版
gRPC 的两大基石:
① HTTP/2 作为传输层,带来:
- 多路复用:一条 TCP 连接上并行跑多个请求,无 HTTP/1.1 的队头阻塞。
- 头部压缩(HPACK):头部开销小。
- 二进制分帧:天然支持流。
- 服务端推送 / 双向流:支撑 gRPC 的流式调用。
② Protobuf(Protocol Buffers)作为接口定义 + 序列化:
先写 .proto 文件定义服务和消息(强类型契约):
service UserService {
rpc GetUser (UserRequest) returns (UserReply);
}
message UserRequest { int32 id = 1; }
message UserReply { string name = 1; int32 age = 2; }
再用 protoc 自动生成客户端/服务端各语言代码。数据在网络上是紧凑的二进制,比 JSON 文本更小、编解码更快。
gRPC 的四种调用模式:
| 模式 | 说明 | 场景 |
|---|---|---|
| 一元(Unary) | 一次请求一次响应(和普通 RPC 一样) | 普通调用 |
| 服务端流 | 一次请求,服务端流式返回多条 | 大结果集、推送 |
| 客户端流 | 客户端流式发多条,服务端一次响应 | 上传、聚合 |
| 双向流 | 双方各自流式收发 | 实时聊天、协作 |
gRPC vs REST/JSON:
| 维度 | gRPC | REST/JSON |
|---|---|---|
| 传输 | HTTP/2 | 多为 HTTP/1.1 |
| 数据格式 | Protobuf(二进制) | JSON(文本) |
| 性能 | 快、小 | 较慢、较大 |
| 契约 | .proto 强类型、生成代码 | 靠文档/OpenAPI,弱约束 |
| 流式 | 原生四种流 | 需 SSE/WebSocket 另搞 |
| 浏览器 | 不能直接用(需 grpc-web) | 天然支持 |
| 可读性/调试 | 差(二进制) | 好(人可读) |
完整版教学
一、先理解 RPC:让远程调用「像本地调用」
gRPC 里的 RPC 是 Remote Procedure Call(远程过程调用)。它的理想是:调用远程服务器上的一个方法,写起来跟调用本地函数一模一样。
// 感觉上就像调本地方法,其实底层发了一次网络请求
UserReply reply = userStub.getUser(request);
背后框架帮你干了一堆脏活:把参数序列化成字节、通过网络发出去、服务端反序列化执行、再把结果序列化回传、客户端反序列化拿到返回值。开发者不用手写这些网络和编解码代码——这就是 RPC 框架的价值。gRPC 是其中的佼佼者,g 常被说成 Google(官方说每个版本 g 有不同含义)。
二、基石一:为什么选 HTTP/2
gRPC 建在 HTTP/2 上,这是它高性能的一半原因。HTTP/1.1 有几个硬伤,HTTP/2 恰好一一解决:
- 队头阻塞:HTTP/1.1 一条连接同一时间基本只能处理一个请求,前一个慢就堵住后面的。HTTP/2 的多路复用让一条 TCP 连接上多个请求并行跑,互不阻塞——微服务间高频调用时优势巨大。
- 头部臃肿:HTTP/1.1 每个请求都带一大坨文本头。HTTP/2 用 HPACK 压缩头部,省带宽。
- 不支持流:HTTP/2 的二进制分帧天然支持在一个「流」上持续收发数据,这正是 gRPC 流式调用的基础。
所以「HTTP/2 的多路复用 + 头部压缩 + 流」直接决定了 gRPC 能又快又支持流式。
三、基石二:Protobuf 为什么比 JSON 强
另一半性能来自 Protobuf 的序列化方式。对比 JSON:
- 体积小:JSON 是文本,字段名(如
"name")每条数据都要重复带一遍,还有引号、括号。Protobuf 是二进制,字段用数字编号(tag)代替字段名,编码紧凑,通常比 JSON 小好几倍。 - 编解码快:解析 JSON 要处理文本、类型推断;Protobuf 二进制格式结构固定,机器解析极快。
- 强类型契约 + 代码生成:
.proto文件是一份机器可读的接口约定,用protoc自动生成各语言的数据类和调用桩。字段类型、结构在编译期就定死,改接口时编译能发现不兼容,比「JSON + 文档」的弱约束可靠得多。 - 向后兼容:字段靠编号标识,新增字段用新编号、老代码忽略不认识的编号,新老版本能共存——微服务灰度升级友好。
代价:Protobuf 是二进制,人不可读,抓包看不懂,调试不如 JSON 直观,需要工具解码。
四、四种流式调用:gRPC 的杀手锏
因为底层是 HTTP/2 的流,gRPC 原生支持四种调用模式,这是 REST 天然不具备的:
- 一元 RPC(Unary):最普通的「一问一答」,和传统函数调用一样。
- 服务端流(Server streaming):客户端发一个请求,服务端持续返回一串响应(如「订阅行情」「大结果集分批返回」)。
- 客户端流(Client streaming):客户端持续发一串请求,服务端最后给一个响应(如「上传大文件分片」「客户端上报聚合」)。
- 双向流(Bidirectional streaming):两边各自独立地流式收发(如实时聊天、协同编辑)。
REST 想做这些,得额外引入 SSE 或 WebSocket,而 gRPC 用一套机制就覆盖了。
五、gRPC 的软肋:为什么它没取代 REST
gRPC 这么强,为什么对外 API 还是 REST 当道?因为它有明确短板:
- 浏览器不能直接用:浏览器的 fetch/XHR 无法完全控制 HTTP/2 底层帧,没法直接发原生 gRPC 请求。要在浏览器用,得通过 grpc-web + 一层代理转换,比较麻烦。
- 可读性/调试差:二进制报文人看不懂,不能像 REST 那样用浏览器地址栏、curl、Postman 直接点一下看结果。
- 生态与通用性:REST + JSON 是事实标准,任何语言、任何工具、任何第三方都能轻松对接;对外开放 API 用 REST 门槛最低。
- 需要
.proto和代码生成:多了一套构建流程,轻量场景显重。
所以业界的典型分工:对内微服务之间用 gRPC(追求性能、强类型、流式);对外开放 API 用 REST/JSON(追求通用、易调试、浏览器友好)。
六、什么时候选 gRPC
- ✅ 内部微服务高频互调——性能敏感、调用量大。
- ✅ 需要流式——服务端推送、双向实时。
- ✅ 多语言团队——用
.proto统一契约、各语言生成代码。 - ✅ 强类型、接口稳定——契约先行、编译期校验。
- ❌ 对外公开 API / 要浏览器直连 / 要便于第三方调试——用 REST 更合适。
七、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 传输 | 基于 HTTP/2 |
| 序列化 | 默认 Protocol Buffers |
| 调用形态 | 一元、服务端流、客户端流、双向流 |
proto -> generate client/server stubs
client stub -> HTTP/2 stream -> server handler
message -> protobuf binary frame
gRPC 的核心组合是 IDL 约束接口、Protobuf 高效编码、HTTP/2 多路复用和流式能力。
- 误区:gRPC 只是换了 JSON 的 HTTP 接口。 它有 IDL、代码生成、HTTP/2 stream、二进制编码和多种流式调用。
- 误区:gRPC 一定比 REST 更适合浏览器。 浏览器原生支持受限,常需要 gRPC-Web 或网关转换。
- 误区:Protobuf 字段顺序随便改都没影响。 字段编号才是兼容关键,删除或复用编号会破坏兼容。
- 追问:HTTP/2 给 gRPC 带来什么? 多路复用、头部压缩、流控和双向流能力。
- 追问:gRPC 如何做版本兼容? 新增可选字段、保留旧字段编号,不复用 tag,服务端保持向后兼容。
- 追问:适合什么场景? 适合内部服务调用、强契约接口、高性能二进制通信和流式数据。
八、加强记忆
gRPC = Google 的高性能 RPC 框架,让远程调用像本地调用。两大基石:HTTP/2 传输(多路复用免队头阻塞、头部压缩、二进制流)+ Protobuf 序列化(二进制紧凑、字段编号、.proto 强类型契约自动生成代码、向后兼容)。相比 REST/JSON 更快更省、有强类型、原生四种流(一元/服务端流/客户端流/双向流)。短板:浏览器不能直连(需 grpc-web)、二进制难调试、通用性不如 REST。分工:对内微服务用 gRPC,对外 API 用 REST。