2. gRPC基础回顾

gRPC 是 Google 开源的高性能远程过程调用(RPC)框架,底层基于 HTTP/2 协议,默认使用 Protocol Buffers(protobuf)作为接口定义语言(IDL)与序列化工具。理解 protobuf 语法和 gRPC 的四种通信模式,是构建 Go 微服务契约压测闭环的前提。

2.1 Protocol Buffers 语法核心

Protocol Buffers 是一种语言中立、平台中立的结构化数据序列化机制,通过 .proto 文件定义服务接口与消息结构。以下是最常用的语法要素:

语法要素 说明 示例
syntax 指定 protobuf 版本,目前主流为 proto3 syntax = "proto3";
package 防止命名冲突,生成代码时映射为 Go 包名 package order.v1;
message 定义数据结构,字段由类型、名称、编号组成 message OrderRequest { string id = 1; }
service 定义 RPC 服务接口,包含方法签名 service OrderService { rpc GetOrder(…) returns (…); }
rpc 声明远程方法,指定输入输出消息类型 rpc GetOrder(OrderRequest) returns (OrderResponse);
repeated 表示字段为数组(列表)类型 repeated string tags = 3;
enum 定义枚举类型,第一个值必须为 0 enum Status { UNKNOWN = 0; ACTIVE = 1; }
字段编号规则:proto3 中每个字段必须分配唯一的数字编号(1 到 536,870,911)。1~15 号字段使用 1 字节编码,16~2047 使用 2 字节编码。高频字段应优先使用小编号以节省带宽。

以下是一个完整的 .proto 文件示例,定义订单查询服务的消息与接口:

syntax = "proto3";
package order.v1;

option go_package = "github.com/example/order/v1;orderpb";

// 订单状态枚举
enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;
  ORDER_STATUS_PENDING     = 1;
  ORDER_STATUS_CONFIRMED   = 2;
  ORDER_STATUS_SHIPPED     = 3;
  ORDER_STATUS_CANCELLED   = 4;
}

// 订单查询请求
message GetOrderRequest {
  string order_id = 1;
}

// 订单查询响应
message GetOrderResponse {
  string       order_id   = 1;
  string       user_id    = 2;
  repeated     string     items      = 3;
  double       total_price = 4;
  OrderStatus  status     = 5;
}

// 订单服务定义
service OrderService {
  rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
}

2.2 gRPC 四种通信模式

gRPC 基于 HTTP/2 的流式传输能力,支持四种通信模式。理解每种模式的适用场景,对后续压测脚本设计与问题排查至关重要。

模式 客户端请求 服务端响应 典型场景
Unary(一元) 单条消息 单条消息 传统 RPC,如查询订单、用户登录
Server Streaming 单条消息 多条消息流 订阅推送、日志拉取、大结果集分页
Client Streaming 多条消息流 单条消息 批量上传、文件分片、聚合计算
Bidirectional Streaming 多条消息流 多条消息流 实时聊天、游戏同步、双向数据管道
压测关注点:流式模式在压测时需额外关注连接复用、流控窗口、内存占用和 goroutine 泄漏。Unary 模式压测相对简单,但流式模式更容易暴露 HTTP/2 帧层问题。

2.3 四种模式的 proto 定义与 Go 代码骨架

以下 proto 定义展示了四种模式在同一个服务中的声明方式:

service StreamDemo {
  // Unary
  rpc UnaryCall(Request) returns (Response);

  // Server Streaming: 客户端发1次,服务端流式返回多条
  rpc ServerStreaming(Request) returns (stream Response);

  // Client Streaming: 客户端流式发送多条,服务端返回1次
  rpc ClientStreaming(stream Request) returns (Response);

  // Bidirectional Streaming: 双方独立流式收发
  rpc BidirectionalStreaming(stream Request) returns (stream Response);
}

Go 服务端实现四种模式的骨架代码:

// Unary
func (s *server) UnaryCall(ctx context.Context, req *pb.Request) (*pb.Response, error) {
  return &pb.Response{Message: "ok"}, nil
}

// Server Streaming
func (s *server) ServerStreaming(req *pb.Request, stream pb.StreamDemo_ServerStreamingServer) error {
  for i := 0; i < 10; i++ {
    if err := stream.Send(&pb.Response{Message: fmt.Sprintf("chunk-%d", i)}); err != nil {
      return err
    }
  }
  return nil
}

// Client Streaming
func (s *server) ClientStreaming(stream pb.StreamDemo_ClientStreamingServer) error {
  var count int
  for {
    _, err := stream.Recv()
    if err == io.EOF {
      return stream.SendAndClose(&pb.Response{Message: fmt.Sprintf("received %d", count)})
    }
    if err != nil {
      return err
    }
    count++
  }
}

// Bidirectional Streaming
func (s *server) BidirectionalStreaming(stream pb.StreamDemo_BidirectionalStreamingServer) error {
  for {
    req, err := stream.Recv()
    if err == io.EOF {
      return nil
    }
    if err != nil {
      return err
    }
    if err := stream.Send(&pb.Response{Message: "echo: " + req.Data}); err != nil {
      return err
    }
  }
}

2.4 四种通信模式交互流程对比

下图以时序视角展示四种模式中客户端与服务端的消息交换过程:

gRPC 四种通信模式交互流程 Unary Client → Server: 1 请求 Server → Client: 1 响应 一次请求-响应完成 Server Streaming Client → Server: 1 请求 Server → Client: N 条流消息 服务端推送数据流 Client Streaming Client → Server: N 条流消息 Server → Client: 1 响应 客户端批量上传 Bidirectional Client ↔ Server: 独立流 双方可同时收发 全双工通信 Client Client Client Client Server Server Server Server 压测关键提示 • Unary 模式:压测时关注 QPS、延迟 P99、错误率,连接池大小直接影响吞吐。 • Server Streaming:关注服务端推送速率、客户端接收缓冲区大小、流控背压。 • Client Streaming:关注客户端发送速率、服务端聚合延迟、内存占用。 • Bidirectional Streaming:关注 goroutine 数量、消息顺序、全双工下的并发安全。

2.5 流式模式的关键概念:流控与背压

gRPC 流式通信依赖 HTTP/2 的流控机制。每个流(stream)都有独立的初始窗口大小(默认 64KB),接收方通过 WINDOW_UPDATE 帧动态调整窗口。当接收方处理速度跟不上发送方时,会触发背压(backpressure),发送方被阻塞直到窗口恢复。

压测常见陷阱:在流式压测中,如果客户端或服务端未正确处理流控,可能出现“假死”现象——连接未断开但数据不再流动。排查时需检查 HTTP/2 帧日志、流窗口大小和 goroutine 栈。

2.6 本章小结

  • Protocol Buffers 是 gRPC 的契约基础,proto3 语法中 message、service、rpc、repeated、enum 是最常用要素。
  • gRPC 四种通信模式(Unary、Server Streaming、Client Streaming、Bidirectional Streaming)覆盖了从传统请求-响应到全双工流的所有场景。
  • 流式模式在压测中需要额外关注流控、背压、goroutine 管理和内存使用。
  • 掌握这些基础,后续章节将基于这些模式设计压测脚本、分析性能瓶颈并排查线上问题。