后端 Web 协议(03):gRPC 与 Protobuf——服务定义、流式通信、性能对比
更新时间:2026-09-01。本文是
backend/web-protocol/Web 协议第 03 篇,接 REST API 设计。gRPC 是 Google 开源的 RPC 框架,基于 HTTP/2 和 Protocol Buffers。相比 REST,gRPC 用 Protobuf 序列化,二进制传输,性能更好,特别适合微服务内部通信。
本文要回答的问题
- Protobuf 是什么?和 JSON 比有什么优势?
- gRPC 服务定义怎么写?proto 文件怎么用?
- 四种流式通信模式是什么?
- gRPC 和 REST 怎么选?
一、Protobuf 序列化
protobuf
// proto 文件定义:user.proto
syntax = "proto3";
package users;
message User {
int32 id = 1;
string name = 2;
string email = 3;
int32 age = 4;
repeated string tags = 5; // 数组
}
// 生成代码:protoc --cpp_out=. user.proto
// 自动生成 User 类,SerializeToString/ParseFromStringProtobuf vs JSON 对比:
| 对比 | JSON | Protobuf |
|---|---|---|
| 大小 | 文本,大 | 二进制,小(比 JSON 小 3-10x) |
| 序列化速度 | 慢(反射解析类型) | 快(数字字段号,直接索引) |
| 可读性 | 可读 | 不可读,需工具 |
| 向后兼容 | 天然,新增字段不破坏 | 字段号机制,新增字段不破坏 |
| 模式定义 | 无 | 强类型,需要 proto 文件 |
二、gRPC 服务定义
protobuf
// 服务定义:greeter.proto
syntax = "proto3";
package greeter;
service Greeter {
// 一元 RPC:请求 + 响应,类似 REST
rpc SayHello (HelloRequest) returns (HelloReply);
// 服务端流式:客户端发一个请求,服务端返回多个消息
rpc LotsOfReplies (HelloRequest) returns (stream HelloReply);
// 客户端流式:客户端发多个消息,服务端返回一个响应
rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply);
// 双向流式:双方都可以发多个消息
rpc BidiHello (stream HelloRequest) returns (stream HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}三、四种流式通信模式
cpp
// 1. 一元 RPC:类似 POST /api/say-hello
// 客户端发一个请求,服务端返回一个响应
auto status = stub.SayHello(&context, request, &reply);
// 2. 服务端流式:类似 SSE 或 WebSocket 单向
// 客户端请求一次,服务端不断返回数据
unique_ptr<ClientReader<HelloReply>> reader(
stub.LotsOfReplies(&context, request));
HelloReply reply;
while (reader->Read(&reply)) {
cout << reply.message() << endl;
}
// 3. 客户端流式:客户端不断上传数据
unique_ptr<ClientWriter<HelloRequest>> writer(
stub.LotsOfGreetings(&context, &reply));
for (const auto& name : names) {
HelloRequest request;
request.set_name(name);
writer->Write(request);
}
writer->WritesDone();
// 4. 双向流式:完全异步,双方自由收发
// 适合聊天、实时数据同步四、gRPC vs REST 对比
| 对比 | REST | gRPC |
|---|---|---|
| 传输协议 | HTTP/1.1 或 HTTP/2 | HTTP/2 |
| 序列化 | JSON(文本) | Protobuf(二进制) |
| 性能 | 慢(JSON 解析开销大) | 快(Protobuf 比 JSON 快 5-10x) |
| 浏览器支持 | 原生支持 | 需 gRPC-Web 代理 |
| 流式 | 不支持(SSE 单向) | 双向流式原生支持 |
| 工具链 | curl / Postman | grpcurl / grpc-ui |
| 调试 | 浏览器直接看 | 需工具翻译 |
选择建议:
- 微服务内部通信:gRPC(性能好,强类型)
- 对外 API:REST(浏览器友好,生态丰富)
- 移动端:gRPC(Protobuf 小,省流量)
- 实时数据流:gRPC(双向流式)
五、gRPC 性能优化要点
cpp
// 1. 连接复用:gRPC 默认复用 HTTP/2 连接
// 2. 流式批量:用流式批量处理,减少连接数
// 3. 压缩:开启 gzip 压缩
grpc::EnableDefaultCompression(true);
// 4. 消息大小限制:默认 4MB,大消息需要调整
channel_args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, 100 * 1024 * 1024);
// 5. 保活:gRPC 有 HTTP/2 的 ping 机制
channel_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 30000);六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| Protobuf 字段号用完 | 新增字段号冲突 | 字段号 1-15 占 1 字节,16-2047 占 2 字节,预留好 |
| 大消息 > 4MB | 连接断开 | 调整 GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH |
| gRPC 服务端和客户端版本不匹配 | 序列化错误 | 确保 proto 文件一致,版本对齐 |
| 浏览器不支持 gRPC | 需要 gRPC-Web | 用 gRPC-Web 代理或 Envoy |
相关与延伸
下一篇:WebSocket 与 SSE——实时通信方案对比、心跳、性能;HTTP/2 协议,见 HTTP 版本演进。
一句话总结
gRPC 与 Protobuf:Protobuf 是二进制序列化,比 JSON 小 3-10x、快 5-10x,字段号保证向后兼容;gRPC 基于 HTTP/2、支持四种流式通信模式(一元、服务端流、客户端流、双向流);微服务内部通信推荐 gRPC,对外 API 推荐 REST,实时数据流用 gRPC 双向流;gRPC 默认复用连接、4MB 消息限制、需调整大消息大小。