3. gRPC 服务定义:service 定义与四种 RPC 类型

gRPC 的核心通信模型基于 Protocol Buffers(protobuf) 的服务定义。在 .proto 文件中,开发者通过 service 关键字声明一个 RPC 服务,并在其中定义若干 RPC 方法。每个方法可以指定四种不同的数据流模式之一,这决定了客户端与服务端之间如何交换消息。

3.1 service 定义基础

一个典型的 gRPC 服务定义包含:包声明、消息类型定义、以及服务接口声明。以下是一个用于压测场景的示例 benchmark.proto:

syntax = "proto3";

package benchmark;

// 请求消息:携带负载大小和唯一标识
message LoadRequest {
  int32 payload_size = 1;   // 负载字节数
  string request_id = 2;    // 请求唯一 ID
  bytes payload = 3;        // 实际负载数据
}

// 响应消息:返回处理结果和耗时
message LoadResponse {
  string request_id = 1;    // 回显请求 ID
  int64 server_timestamp = 2; // 服务端处理时间戳
  bool success = 3;         // 处理是否成功
}

// 压测服务定义:包含四种 RPC 模式
service BenchmarkService {
  // 一元 RPC
  rpc UnaryCall(LoadRequest) returns (LoadResponse);

  // 服务端流式 RPC
  rpc ServerStreamingCall(LoadRequest) returns (stream LoadResponse);

  // 客户端流式 RPC
  rpc ClientStreamingCall(stream LoadRequest) returns (LoadResponse);

  // 双向流式 RPC
  rpc BidirectionalStreamingCall(stream LoadRequest) returns (stream LoadResponse);
}
压测设计要点:在压测场景中,LoadRequest 中的 payload 字段用于模拟不同大小的数据传输负载,request_id 用于追踪每个请求的完整生命周期,便于后续分析延迟和吞吐量。

3.2 四种 RPC 类型详解

gRPC 基于 HTTP/2 的流式传输能力,提供了四种 RPC 通信模式。下表从数据流方向、适用场景和压测关注点三个维度进行对比:

RPC 类型 数据流方向 典型场景 压测关注点
一元 RPC 客户端发送 1 个请求 → 服务端返回 1 个响应 简单查询、认证、配置获取 请求/响应延迟、吞吐量(QPS)
服务端流式 RPC 客户端发送 1 个请求 → 服务端返回多个响应 日志推送、数据订阅、大文件分片下载 流式吞吐量、背压机制、内存占用
客户端流式 RPC 客户端发送多个请求 → 服务端返回 1 个响应 批量数据上传、传感器数据聚合 批量写入速率、服务端聚合效率
双向流式 RPC 客户端和服务端可同时发送多个消息 实时聊天、协同编辑、实时数据管道 全双工吞吐量、消息顺序性、并发控制

3.3 四种 RPC 模式的通信流程

下面的 SVG 图展示了四种 RPC 模式在客户端与服务端之间的消息交换流程,帮助理解不同流式模式下的数据流动方向:

四种 gRPC RPC 模式通信流程对比 客户端 服务端 ① 一元 RPC 单个请求 单个响应 ② 服务端流式 RPC 单个请求 多个响应(流) ③ 客户端流式 RPC 多个请求(流) 单个响应 ④ 双向流式 RPC 多个请求(流) 多个响应(流)

3.4 流式 RPC 的底层机制

理解流式 RPC 的底层机制对于压测设计至关重要。以下 SVG 图展示了 gRPC 在 HTTP/2 流之上如何实现四种 RPC 模式的消息帧传输:

gRPC 基于 HTTP/2 流的消息帧传输机制 HTTP/2 连接(单一 TCP 连接) 流 1:一元 RPC 请求帧 → 响应帧(流关闭) 流 2:服务端流式 请求帧 → 响应帧1 → 响应帧2 → ... → 流关闭 流 3:客户端流式 请求帧1 → 请求帧2 → ... → 响应帧(流关闭) 流 4:双向流式 请求帧1 ↔ 响应帧1 → 请求帧2 ↔ 响应帧2 ... 压测关键理解 • 所有 RPC 类型共享同一个 HTTP/2 连接,通过流 ID 区分不同的 RPC 调用。 • 流式 RPC 在单个流上传输多个消息帧,帧的发送和接收是异步的。 • 双向流式 RPC 允许客户端和服务端同时发送帧,实现真正的全双工通信。

3.5 四种 RPC 的代码骨架对比

在 Python 的 grpc 库中,四种 RPC 模式的服务端实现具有不同的迭代器/生成器模式。以下代码片段展示了服务端处理四种 RPC 的核心逻辑:

import grpc
from concurrent import futures
import benchmark_pb2
import benchmark_pb2_grpc

class BenchmarkServiceServicer(benchmark_pb2_grpc.BenchmarkServiceServicer):

    # 1. 一元 RPC:接收一个请求,返回一个响应
    def UnaryCall(self, request, context):
        response = benchmark_pb2.LoadResponse(
            request_id=request.request_id,
            server_timestamp=time.time_ns(),
            success=True
        )
        return response

    # 2. 服务端流式 RPC:接收一个请求,通过 yield 返回多个响应
    def ServerStreamingCall(self, request, context):
        for i in range(10):  # 模拟发送 10 个响应
            response = benchmark_pb2.LoadResponse(
                request_id=f"{request.request_id}-{i}",
                server_timestamp=time.time_ns(),
                success=True
            )
            yield response

    # 3. 客户端流式 RPC:通过 for 循环接收多个请求,返回一个响应
    def ClientStreamingCall(self, request_iterator, context):
        total_requests = 0
        for request in request_iterator:
            total_requests += 1
            # 处理每个请求...
        response = benchmark_pb2.LoadResponse(
            request_id=f"batch-{total_requests}",
            server_timestamp=time.time_ns(),
            success=True
        )
        return response

    # 4. 双向流式 RPC:同时接收和发送流
    def BidirectionalStreamingCall(self, request_iterator, context):
        for request in request_iterator:
            response = benchmark_pb2.LoadResponse(
                request_id=request.request_id,
                server_timestamp=time.time_ns(),
                success=True
            )
            yield response
压测陷阱提示:在压测流式 RPC 时,务必注意 背压(backpressure) 机制。如果客户端发送速率超过服务端处理能力,gRPC 的流控机制会阻塞发送方。在压测脚本中,应监控 grpc.FlowControlException 或设置合理的 grpc.Compression 选项,避免因流控导致压测结果失真。

3.6 四种 RPC 的压测指标差异

不同的 RPC 模式在压测时需要关注不同的性能指标。下表总结了四种模式的核心压测指标:

RPC 类型 核心指标 次要指标 典型瓶颈
一元 RPC QPS、P99 延迟 错误率、连接数 CPU 序列化/反序列化
服务端流式 消息吞吐量(msg/s) 流持续时间、内存占用 网络带宽、GC 暂停
客户端流式 批量写入速率(req/s) 服务端聚合延迟 服务端缓冲区大小
双向流式 全双工吞吐量 消息顺序性、并发数 HTTP/2 流并发限制

3.7 本章小结

gRPC 的四种 RPC 模式为不同数据传输场景提供了灵活的通信原语。在压测实践中,需要根据 RPC 模式的特点设计对应的压测策略:一元 RPC 关注请求/响应延迟分布,流式 RPC 关注吞吐量和背压行为。理解 service 定义和四种 RPC 的底层机制,是构建高效压测脚本的基础。