2. gRPC 基础回顾:核心概念与通信模式

在深入缓存一致性方案之前,必须牢固掌握 gRPC 的基础构件。本章将快速回顾 gRPC 的定位、Protocol Buffers 的契约定义方式,以及四种 RPC 模式的适用场景。这些知识是后续实现缓存失效广播、分布式锁、一致性哈希通信的底层支撑。

2.1 gRPC 简介

gRPC 是 Google 开源的高性能、跨语言 RPC 框架,基于 HTTP/2 协议传输,默认使用 Protocol Buffers 作为接口定义语言(IDL)和序列化工具。其核心优势包括:

  • HTTP/2 多路复用:单一 TCP 连接可并发处理多个请求/响应,降低连接开销。
  • 双向流式通信:服务端与客户端可同时发送数据流,适合缓存订阅/推送场景。
  • 强类型契约:通过 .proto 文件定义服务与消息,生成各语言客户端/服务端桩代码。
  • 负载均衡与健康检查:原生支持 gRPC 负载均衡策略,适配缓存节点动态扩缩。
缓存场景提示:gRPC 的双向流和 HTTP/2 头部压缩特性,特别适合缓存节点间频繁、小数据量的失效通知与元数据同步。

2.2 Protocol Buffers 定义

Protocol Buffers(proto3)是 gRPC 的契约语言。一个典型的 .proto 文件包含消息类型和服务定义。以下是与缓存一致性相关的示例:

syntax = "proto3";

package cache.v1;

// 缓存键值对消息
message CacheEntry {
  string key = 1;
  bytes value = 2;
  int64 ttl_seconds = 3;       // 过期时间(秒)
  int64 version = 4;           // 版本号,用于冲突检测
}

// 缓存失效通知
message InvalidationEvent {
  string cache_key = 1;
  string region = 2;           // 缓存区域标识
  int64 timestamp = 3;         // 失效发生时间戳
  enum Reason {
    UNKNOWN = 0;
    UPDATE = 1;
    DELETE = 2;
    EVICTION = 3;
  }
  Reason reason = 4;
}

// 缓存一致性服务
service CacheConsistency {
  // 单向失效通知
  rpc Invalidate(InvalidationEvent) returns (InvalidationAck);
  // 双向流订阅失效事件
  rpc SubscribeInvalidations(SubscriptionRequest) returns (stream InvalidationEvent);
}
元素 说明 缓存一致性用途
message 定义结构化数据字段 封装缓存条目、失效事件、订阅请求
service 定义 RPC 方法签名 声明失效通知、订阅、批量同步接口
enum 枚举常量 区分失效原因(更新/删除/驱逐)
stream 标记流式返回 实现持续推送缓存失效事件

2.3 四种 RPC 模式

gRPC 定义了四种通信模式,每种模式在缓存一致性架构中有不同的适用位置。下面通过对比表格和一张架构图来理解它们的差异。

模式 客户端请求 服务端响应 缓存一致性典型场景
Unary 单条 单条 查询缓存条目、单次失效确认
Server Streaming 单条 多条(流) 订阅某个缓存区域的批量失效事件
Client Streaming 多条(流) 单条 批量上报缓存命中/未命中统计
Bidirectional Streaming 多条(流) 多条(流) 缓存节点间双向同步、分布式锁协商
四种 gRPC 通信模式对比 客户端 服务端 请求 (1) 响应 (1) Unary 客户端 服务端 请求 (1) 响应 (N) Server Streaming 客户端 服务端 请求 (N) 响应 (1) Client Streaming 客户端 服务端 请求 (N) 响应 (N) Bidirectional 缓存一致性中,Server Streaming 用于失效事件订阅,Bidirectional Streaming 用于节点间双向同步。 Unary 适合单次查询/确认,Client Streaming 适合批量上报。

2.4 模式选择与缓存一致性映射

在实际的缓存一致性系统中,四种模式并非孤立使用,而是根据通信方向和数据量组合出现。下面通过第二个 SVG 图展示一个典型的缓存节点间通信架构,其中标注了每种模式的使用位置。

缓存一致性架构中的 gRPC 模式分布 应用服务 (Unary 查询缓存) 缓存节点 A (主副本) 缓存节点 B (从副本) Unary Server Streaming 缓存节点 A (双向同步发起方) 缓存节点 B (双向同步响应方) Bidirectional Streaming (数据同步 + 确认) 监控客户端 (批量上报命中率) Client Streaming Unary 用于应用查询缓存;Server Streaming 用于从副本订阅主副本的失效事件; Bidirectional Streaming 用于节点间全量同步;Client Streaming 用于监控客户端批量上报指标。

2.5 关键代码片段:Unary 与 Server Streaming 实现

以下代码展示在 Go 语言中如何实现 Unary 模式的缓存查询和 Server Streaming 模式的失效事件订阅。注意流式上下文的处理方式。

// Unary 模式:查询缓存条目
func (s *CacheServer) GetCacheEntry(ctx context.Context, req *pb.GetRequest) (*pb.CacheEntry, error) {
    entry, err := s.store.Get(req.Key)
    if err != nil {
        return nil, status.Errorf(codes.NotFound, "key not found: %v", err)
    }
    return entry, nil
}

// Server Streaming 模式:订阅失效事件
func (s *CacheServer) SubscribeInvalidations(req *pb.SubscriptionRequest, stream pb.CacheConsistency_SubscribeInvalidationsServer) error {
    // 注册订阅者
    ch := s.broker.Subscribe(req.Region)
    defer s.broker.Unsubscribe(req.Region, ch)

    for {
        select {
        case <-stream.Context().Done():
            // 客户端断开连接
            return nil
        case event := <-ch:
            // 向客户端推送失效事件
            if err := stream.Send(event); err != nil {
                return err
            }
        }
    }
}
流式连接管理要点:Server Streaming 和 Bidirectional Streaming 中,必须监听 stream.Context().Done() 以检测客户端断开,避免 goroutine 泄漏。同时建议在服务端设置流超时和最大并发流数,防止资源耗尽。

2.6 本章小结

  • gRPC 基于 HTTP/2 和 Protocol Buffers,提供四种通信模式,覆盖缓存一致性中的查询、订阅、上报和双向同步需求。
  • Protocol Buffers 的 message、service、enum、stream 关键字是定义缓存一致性契约的核心工具。
  • Unary 适合单次请求/响应;Server Streaming 适合事件推送;Client Streaming 适合批量上报;Bidirectional Streaming 适合节点间对等同步。
  • 在后续章节中,将基于这些模式构建缓存失效广播、分布式锁协商、一致性哈希通信等高级机制。