第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核心概念总览 服务定义(Service) Protobuf 四种调用模式 一元调用 服务端流式 客户端流式 双向流式 说明:服务定义依赖Protobuf描述数据结构,并声明四种调用模式。 选择哪种模式取决于业务需要的交互方式:查询用一元,推送用服务端流,上传用客户端流,聊天用双向流。

拿到一个gRPC项目,先画服务定义。然后选一种调用模式,配合Protobuf写出数据结构。别一开始就陷入语言细节。把这三个概念理清,gRPC的架子就搭起来了。