第4章 gRPC核心概念:Protobuf、服务定义、四种调用模式
聊完RESTful,咱们该看看gRPC了。你可能听说过gRPC性能好、效率高,但到底怎么用?它的核心概念是什么?我把自己的理解拆成三块来讲:Protocol Buffers(protobuf)、服务定义以及四种调用模式。嗯,把这三点吃透了,你就拿到了gRPC的钥匙。
4.1 理解Protocol Buffers(Protobuf)
gRPC默认用Protobuf做序列化协议。说白了,它就是一套定义数据结构和序列化规则的“语言”。你想想看,传统的JSON是文本格式,就算只传一个数字,也得带上花括号和冒号。Protobuf是二进制格式,体积小、解析快。
4.1.1 定义消息结构
我在项目中接触Protobuf的第一反应是:“这不就是接口文档吗?”确实,你把数据结构写在.proto文件里,双方都看它来生成代码。比如一个简单的用户消息:
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
每个字段都有唯一的编号(1、2、3)。这个编号很重要,它是Protobuf在二进制中标识字段的“标签”。
关键点:字段编号在1-15范围内只用1个字节编码,16-2047用2个字节。所以,频繁使用的字段编号尽量用小的。
4.1.2 为什么用Protobuf
- 体积小:二进制格式,比JSON小3-10倍。
- 速度快:解析不需要语法分析树。
- 向前/向后兼容:字段编号让新旧版本能共存。
- 强类型:编译期就做类型检查,减少运行时错误。
我曾经在数据管道项目里,把JSON换成Protobuf后,单个消息大小从2KB降到200字节,吞吐量直接翻了4倍。嗯,数据不会骗人。
小技巧:用protoc命令生成多种语言代码。我个人习惯把.proto文件放在独立仓库,作为接口契约共享给团队。
4.2 理解服务定义(Service Definition)
有了数据,还得定义服务。Protobuf里用service关键字描述API端点。定义放在同一个.proto文件中,形成完整的“服务契约”。
4.2.1 定义一个简单的gRPC服务
service UserService {
rpc GetUser (GetUserRequest) returns (User);
}
message GetUserRequest {
int32 user_id = 1;
}
这里的UserService就是服务名,GetUser是RPC方法名。它接受一个GetUserRequest,返回一个User。整个交互模型是“请求-响应”。
你可能会问:“这和RESTful有什么区别?”我举个例子:RESTful里你定义/users/{id},而RPC风格写/UserService/GetUser。但gRPC的威力不在于命名,而在于背后的四种调用模式。
避坑指南:我曾经在定义服务时,把方法名写成Java的驼峰风格,结果生成的C#代码出现命名冲突。记得遵循UpperCamelCase,并在.proto文件中保持统一。
4.3 四种调用模式
gRPC支持四种RPC调用模式。每种解决不同的实际问题。我从最简单的开始讲。
4.3.1 一元调用(Unary RPC)
这就是常规的请求-响应。客户端发送一个请求,服务端返回一个响应。跟RESTful的GET请求很像,只是底层走的是Protobuf。
rpc GetUser (GetUserRequest) returns (User);
我经常用一元调用做查询、操作单个资源。
4.3.2 服务端流式调用(Server Streaming RPC)
客户端发一个请求,服务端返回一个流。比如拉取用户的所有订单:
rpc ListOrders (ListOrdersRequest) returns (stream Order);
高效解析:客户端不需要等所有订单都拉完再处理,每次都实时接收并打印。这在数据上报、日志推送场景特别有用。
4.3.3 客户端流式调用(Client Streaming RPC)
客户端发送一个流,服务端返回一个响应。比如批量上传文件:
rpc UploadFiles (stream FileChunk) returns (UploadStatus);
客户端不断发送数据流,服务端边接收边处理,最后返回一个状态。做过大文件上传的朋友应该懂,这能省掉很多分片的麻烦。
4.3.4 双向流式调用(Bidirectional Streaming RPC)
客户端和服务端同时发送/接收流。两端都有自己的流,各写各的。常见的例子是聊天应用:
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
我经历过一个实时监控系统,用双向流实现服务端推送监控指标,客户端实时反馈订阅动作。效果比长轮询流畅得多。
| 调用模式 | 客户端发送 | 服务端返回 | 典型场景 |
|---|---|---|---|
| 一元调用 | 一个请求 | 一个响应 | 查询、写入 |
| 服务端流式 | 一个请求 | 多个响应(流) | 日志拉取、数据推送 |
| 客户端流式 | 多个请求(流) | 一个响应 | 批量上传、流式写入 |
| 双向流式 | 多个请求(流) | 多个响应(流) | 实时聊天、协同编辑 |
总结:四种模式覆盖了从简单查询到复杂实时通信的全部场景。掌握后,你就能根据业务灵活选择。
拿到一个gRPC项目,先画服务定义。然后选一种调用模式,配合Protobuf写出数据结构。别一开始就陷入语言细节。把这三个概念理清,gRPC的架子就搭起来了。