3. gRPC 服务定义:service 关键字与 RPC 方法类型
在 gRPC 的接口契约体系中,service 关键字是定义远程过程调用的核心入口。它通过 Protocol Buffers(Protobuf)的 IDL(接口定义语言)将一组相关的 RPC 方法聚合为一个逻辑服务单元。每个 RPC 方法必须明确指定其通信模式,这直接决定了客户端与服务端之间的数据流形态。
3.1 service 关键字与基本语法
在 .proto 文件中,使用 service 关键字声明一个 gRPC 服务,内部包含一个或多个 rpc 方法定义。每个方法需指定请求消息类型和响应消息类型。
service ServiceName {
rpc MethodName(RequestMessage) returns (ResponseMessage);
}
其中 RequestMessage 和 ResponseMessage 必须是已在同一 .proto 文件或导入文件中定义的 Protobuf 消息类型。
以下是一个报表导出场景中的服务定义示例:
syntax = "proto3";
package reportexport;
// 导出任务相关的消息定义
message ExportRequest {
string report_id = 1;
string format = 2; // pdf, csv, xlsx
int32 page_size = 3;
}
message ExportResponse {
string task_id = 1;
ExportStatus status = 2;
}
message ExportStatus {
enum State {
PENDING = 0;
PROCESSING = 1;
COMPLETED = 2;
FAILED = 3;
}
State state = 1;
float progress = 2; // 0.0 ~ 1.0
string download_url = 3;
}
// 定义导出服务
service ReportExportService {
// 同步导出(Unary)
rpc SyncExport(ExportRequest) returns (ExportResponse);
// 订阅导出进度(Server Streaming)
rpc SubscribeExportProgress(ExportRequest) returns (stream ExportStatus);
// 批量提交导出任务(Client Streaming)
rpc BatchSubmitExport(stream ExportRequest) returns (ExportResponse);
// 实时双向导出控制(Bidirectional Streaming)
rpc LiveExportControl(stream ExportRequest) returns (stream ExportStatus);
}
3.2 四种 RPC 方法类型详解
gRPC 基于 HTTP/2 的流式传输能力,定义了四种 RPC 方法类型。理解它们的差异是设计高效接口契约的前提。
| 方法类型 | 请求流 | 响应流 | 典型场景 | 数据交互特点 |
|---|---|---|---|---|
| Unary | 单条消息 | 单条消息 | 简单查询、同步导出、用户认证 | 类 HTTP 请求/响应,最常用 |
| Server Streaming | 单条消息 | 多条消息(流) | 导出进度推送、日志订阅、实时监控 | 服务端主动推送,客户端被动接收 |
| Client Streaming | 多条消息(流) | 单条消息 | 批量数据上传、大文件分片提交 | 客户端批量发送,服务端汇总响应 |
| Bidirectional Streaming | 多条消息(流) | 多条消息(流) | 实时控制台、双向数据同步、交互式导出 | 全双工通信,两端独立读写 |
3.3 四种方法类型的流式交互模型
下图展示了四种 RPC 方法在客户端与服务端之间的消息流方向与生命周期差异。箭头方向表示数据流动方向,矩形代表消息单元。
3.4 流式方法在导出场景中的选择策略
在报表导出架构中,不同导出阶段对通信模式有不同要求。以下策略可帮助你在接口契约设计时做出正确选择:
- 同步导出(Unary):适用于小报表(数据量 < 10MB),客户端发起请求后阻塞等待完整响应。优点是实现简单,缺点是长时间占用连接。
- 进度订阅(Server Streaming):适用于大报表导出,客户端发起一次请求后,服务端持续推送
ExportStatus消息,包含进度百分比和当前阶段描述。客户端可实时更新 UI 进度条。 - 批量提交(Client Streaming):适用于客户端需要分片上传大量导出参数或数据源的场景。例如,用户一次性勾选 1000 个报表 ID,客户端分批发送,服务端汇总后返回一个总任务 ID。
- 实时控制(Bidirectional Streaming):适用于交互式导出控制台。客户端可以随时发送暂停、取消、修改参数等指令,服务端同时返回当前状态和结果流。这是最灵活但也是最复杂的模式。
3.5 流式方法的错误处理与生命周期
流式 RPC 的错误处理与 Unary 有显著差异。服务端可以在流中的任意时刻发送错误,客户端需要正确处理流的中断和重连。
| 场景 | Unary | Server Streaming | Client Streaming | Bidirectional Streaming |
|---|---|---|---|---|
| 错误时机 | 响应返回时 | 流中任意位置 | 客户端发送完毕后 | 流中任意位置 |
| 客户端处理 | 检查状态码 | 监听 onError 回调 | 等待服务端响应 | 监听 onError 回调 |
| 重连策略 | 直接重试 | 重新发起订阅 | 重新发送所有数据 | 重新建立双向流 |
| 幂等性要求 | 低 | 中(需去重) | 高(需去重) | 高(需状态同步) |
下图展示了在导出进度订阅场景中,Server Streaming 模式下服务端如何通过流式消息逐步推送状态,以及客户端如何处理流中断与重连:
ExportStatus 消息中增加 sequence_number 字段用于去重。
3.6 方法类型选择决策树
以下决策树可帮助你在设计新接口时快速确定应使用哪种 RPC 方法类型:
- 客户端是否需要发送多条消息?
- 否 → 进入第 2 步
- 是 → 进入第 3 步
- 服务端是否需要返回多条消息?
- 否 → Unary
- 是 → Server Streaming
- 服务端是否需要返回多条消息?
- 否 → Client Streaming
- 是 → Bidirectional Streaming
这个决策树覆盖了 90% 以上的接口设计场景。在报表导出架构中,Unary 用于简单查询和同步导出,Server Streaming 用于进度推送,Client Streaming 用于批量提交,Bidirectional Streaming 用于交互式控制台。