文章

Redis Cluster Gossip 协议深度解析

Redis Cluster Gossip 协议深度解析

1. 协议背景与设计哲学

在分布式系统中,元数据(Metadata)的维护通常有两种模式:

  1. 集中式(Centralized):如使用 ZooKeeper、Etcd。
    • 优点:元数据强一致,时效性好。
    • 缺点:存在单点瓶颈,更新元数据时压力全在中心节点,大规模集群下存储压力大。
  2. 去中心化(Decentralized/P2P):即 Redis Cluster 采用的 Gossip 协议
    • 优点:无中心节点,扩展性强,任何节点宕机不影响整体元数据传播。
    • 缺点:元数据收敛有延迟(最终一致性),存在“网络风暴”风险。

Redis Cluster 选择 Gossip(谣言传播协议)的核心目的是为了实现高可用可扩展性,允许集群在没有外部依赖的情况下自动发现节点、检测故障并进行故障转移。


2. 通信机制与端口

2.1 16379 端口

每个 Redis Cluster 节点除了开放用于客户端连接的业务端口(如 6379),还会额外开启一个集群总线端口(Cluster Bus Port)

  • 规则:通常是业务端口 + 10000(例如 16379)。
  • 协议:使用二进制协议(Binary Protocol),而非文本协议(RESP)。这通过减少带宽占用和序列化开销,极大提升了节点间的通信效率。

2.2 交互频率

Redis 节点会不断地与其他节点交换信息。

  • 频率:默认每 100ms 执行一次 PING 发送逻辑(由 clusterCron 函数驱动)。
  • 补充说明clusterCron 不仅负责发送 PING,还承担故障转移触发、节点重连、槽位更新等核心集群维护任务。
  • 策略:每次选取目标节点发送 PING 消息,并接收 PONG 消息。

3. 消息类型详解

Redis Gossip 协议定义了多种消息类型,用于不同的交互场景。最核心的有四种:MEET、PING、PONG、FAIL

3.1 MEET(握手)

  • 作用:用于邀请新节点加入集群。
  • 场景:当管理员执行 CLUSTER MEET ip port 命令时。
  • 流程:节点 A 向节点 B 发送 MEET。B 收到后认为 A 是集群成员,并回复 PONG。此后,A 和 B 会通过 Gossip 协议将彼此的存在传播给集群中的其他节点。

3.2 PING(探测)

  • 作用:集群内最频繁的消息,用于检测节点是否在线以及交换状态信息。
  • 内容
    • 自身状态:我是谁,我是 Master 还是 Slave,我负责哪些 Slot,我的 configEpoch 是多少。
    • Gossip section:我还知道哪些节点的信息(随机携带集群中部分节点信息)。

3.3 PONG(回复)

  • 作用:对 MEET 或 PING 的响应。
  • 内容:与 PING 消息结构完全相同,包含自身状态和已知的其他节点信息。
  • 特殊用法:当节点配置发生变更(如 Slot 迁移完成、Master 故障转移后),节点会通过正常的 PING/PONG 消息让集群逐步感知最新状态。

3.4 FAIL(下线广播)

  • 作用:当一个节点判定另一个节点已经“客观下线”(FAIL)时,会向集群广播 FAIL 消息。
  • 特点:FAIL 消息通过 clusterBroadcastMessage 基于已有集群连接尽可能发送给所有已知节点,但不保证强一致的全网强制覆盖(若部分节点当前不可达则无法送达),最终仍依赖 Gossip 协议实现状态的最终一致。

4. 消息结构深度剖析

这是 Gossip 协议最硬核的部分。一个 Gossip 消息分为 消息头(Header)消息体(Data)

4.1 消息头(ClusterMsg)

每个 PING/PONG 包都包含发送者自己的详细信息。这意味着,只要接收到一个包,就能更新发送者的最新状态。 核心字段包括:

  • Node ID:节点的唯一标识(40位十六进制字符)。
  • currentEpoch & configEpoch:两个关键的纪元(逻辑时钟),用于故障转移和冲突仲裁(下文详解)。
  • Bitmap (myslots):一个 2KB 的位图(16384 bit)。如果某一位是 1,表示该 Slot 由发送者负责。
    • 说明:16384 个 Slot 是在分布精度和通信开销之间的工程权衡,而非被位图大小强制决定。选择 16384 既保证了够用的哈希分布粒度、可接受的元数据传播成本,也便于快速位图运算和控制集群重分片成本。若使用 65536 个 Slot,位图将达到 8KB,虽不会直接导致“严重网络风暴”,但会线性增加带宽成本。
  • Flags:标识节点角色(Master/Slave)、状态(PFAIL/FAIL)等。
  • Slaveof:如果发送者是 Slave,这里记录其 Master 的 Node ID。

4.2 消息体(Gossip Section)

除了发送者自己的信息,PING/PONG 包还会携带它所知的其他节点的信息。这部分称为 Gossip Section。

  • 包含数量:默认包含集群总节点数的约 1/10,但受最大包长和优先节点影响(至少 3 个)。
  • 内容:节点 ID、IP、Port、Flags(状态)、最后一次接收 PONG 的时间等。
  • 作用:实现了“谣言传播”。比如 A 告诉 B:“我知道 C 现在的状态是 PFAIL”,B 收到后会更新本地关于 C 的记录。

5. 节点选择算法(防止网络风暴)

如果集群有 N 个节点,每个节点都向所有其他节点发送 PING,网络流量将是 $O(N^2)$,这会导致网络拥塞。Redis 采用了精妙的算法来控制流量。

clusterCron 定时任务中:

  1. 抽样选择:随机抽样若干个节点(抽样数量基于源码常量 CLUSTER_PING_SAMPLE_COUNT,通常为 5 个左右),从中选出最久没有通信的那个节点,发送 PING。
  2. 兜底发送:如果发现某个节点通信超时时间超过了 cluster_node_timeout / 2,为了防止误判其下线,会立即向其发送 PING。

结论:这个机制保证了在 cluster_node_timeout 时间内,节点至少会被检测一次,同时也限制了网络包的数量。


6. 故障检测机制(PFAIL 与 FAIL)

这是 Gossip 协议最关键的功能:如何判断一个节点挂了?

6.1 PFAIL:主观下线 (Subjective Down)

  • 定义:某个节点(比如 A)认为另一个节点(比如 B)不可用。
  • 触发条件:A 向 B 发送 PING,在 cluster_node_timeout 时间内未收到 B 的 PONG 回复。
  • 动作:A 在本地的节点列表中,将 B 标记为 PFAIL

6.2 FAIL:客观下线 (Objective Down)

  • 定义:集群中超过半数当前存活且有投票权的 Master 节点都认为 B 挂了,那么 B 就真的挂了(Slave 不参与 FAIL 判定)。
  • 流转过程
    1. A 将 B 标记为 PFAIL
    2. A 通过 Gossip 消息(PING/PONG)将“B 是 PFAIL”的信息传播给 C、D、E…
    3. C、D、E 收到消息后,在本地维护一个“下线报告链表(Failure Reports)”。
    4. 当 A 发现,在 cluster_node_timeout * 2 的时间窗口内,收集到的关于 B 的 PFAIL 报告数量满足 failure_reports >= majority_of_masters(即超过半数有投票权的 Master)。
    5. A 将 B 的状态升级为 FAIL
    6. A 向集群广播 FAIL 消息。
    7. 所有收到 FAIL 消息的节点,立即将 B 标记为 FAIL(无需再通过 Gossip 累积,强行覆盖)。

7. 纪元(Epoch):分布式一致性的基石

在去中心化系统中,如何解决冲突?比如两个节点同时宣称自己是某个 Slot 的主节点。Redis 引入了逻辑时钟概念,称为 Epoch

7.1 currentEpoch(集群当前纪元)

  • 类似于集群的全局逻辑时钟,用于标识集群状态的版本演进。
  • 遵循“最大者胜”原则:节点间交互时,若发现对方的 currentEpoch 更大,会立即同步更新自己的 currentEpoch

7.2 configEpoch(配置纪元)

  • 关联到具体的节点(主要是 Master),表示该节点负责的 Slot 信息的版本号。
  • 故障转移与冲突解决
    • 当 Slave 试图发起故障转移晋升为 Master 时,Slave 发起 failover,向 master 请求投票,如果赢得多数,它会获得一个新的 configEpoch(通过 bump epoch 机制)。
    • 当节点 A 和节点 B 都宣称自己负责 Slot 100 时,集群会比较 A 和 B 的 configEpoch,谁的 configEpoch 更大,谁就是 Slot 100 的真正主人,小的那个会被强制更新配置。

8. Gossip 协议的优缺点与生产痛点

8.1 优点

  1. 去中心化:极其健壮,无单点故障。
  2. 扩展性:支持数百个节点的规模(官方建议线性扩展至 1000 节点,生产实践中为控制开销常不超过 500 节点)。
  3. 容错性:任何节点都可以处理请求并重定向(MOVED/ASK),元数据最终一致。
  4. 无外部依赖:Redis Cluster 的设计目标是不依赖 ZooKeeper、Etcd 等外部协调组件(二者设计哲学不同:ZooKeeper 提供强一致 CP,Redis Cluster 是 AP + 最终一致)。

8.2 缺点与痛点

  1. 网络风暴 (Network Storm)
    • 随着节点数 N 增加,Gossip 消息包体增大(因为要携带其他节点信息)。
    • 虽然 Redis 限制了 Gossip section 的大小,但在 500+ 节点的大集群中,带宽占用依然可观。
    • 网络复杂度说明:在稳态下,每节点每秒约 ping 1 个节点,总通信复杂度接近 O(N);但在大规模 FAIL 广播等最坏情况下通信复杂度可接近 O(N²),但持续时间短且非稳态行为。
  2. 收敛慢 (Convergence Latency)
    • 节点故障信息需要通过 Gossip 传播。
    • cluster_node_timeout 设置较大(如 15s)时,故障发现和转移可能需要数十秒,期间服务不可用。
  3. 假死判定
    • 如果 Master 节点负载过高(CPU 100%)无法及时回复 PING,可能被集群误判为 FAIL 并触发不必要的故障转移。

9 Redis Cluster Gossip 协议 与 Raft 协议对比

以下对比基于 Redis Cluster 的 Gossip + 多数派纪元机制(Hybrid 模型)标准 Raft 强一致性协议,围绕一致性语义、传播机制、故障处理与扩展性进行对标。

1. 一致性模型与核心设计目标

  • Redis Cluster

    • 整体属于 AP 倾向的系统(CAP 语境下)。
    • 设计目标是去中心化与高可用,允许网络分区期间继续对外提供服务。
    • 元数据(节点状态、slot 映射)是最终一致。
    • 不保证跨分区的强一致写入安全。
  • Raft

    • 明确的 CP 模型。
    • 设计目标是提供线性一致(linearizable)的状态机复制。
    • 任意写入必须经过多数派确认。
    • 在多数派不可用时主动拒绝写入。

核心差异:Redis 优先保证“可写”;Raft 优先保证“正确”。

2. 元数据传播与收敛机制

  • Redis Cluster

    • 节点状态通过 Gossip 随机扩散(PING/PONG)。
    • 收敛时间取决于网络状况与 cluster_node_timeout。
    • 不存在单一“提交点”。
    • 元数据是逐步传播并最终统一。

    需要强调: Redis 的 slot ownership 最终收敛依赖 configEpoch 的比较,而不是单纯依赖 gossip。

  • Raft

    • 所有变更必须由 Leader 产生。
    • 通过 AppendEntries 复制日志。
    • 日志条目经多数派确认后才算 committed。
    • 收敛边界清晰:commit index 是一致性屏障。

核心差异:Redis 没有“全局提交点”;Raft 有明确的提交线。

3. 故障检测与故障转移逻辑

  • Redis Cluster

    • 故障检测是分布式的:PFAIL → 收集 failure report → FAIL。
    • FAIL 判定需要多数 master 认可。
    • 故障转移由从节点发起选举。
    • 无固定协调者,完全去中心化。
    • 选举成功的从节点获得新的 configEpoch。

    本质上是:

    Gossip 负责传播状态 多数派 + 纪元机制负责最终裁决

  • Raft

    • Leader 定期发送心跳。
    • 心跳超时触发选举。
    • 新 Leader 必须获得多数派投票。
    • 故障检测与选举机制严格绑定在 term 上。

核心差异:Redis 是“检测分散,裁决多数”;Raft 是“检测集中,裁决多数”。

4. 集群规模与扩展性

  • Redis Cluster

    • 去中心化心跳机制允许更大规模。
    • 官方建议规模 < 1000。
    • 实践中常控制在数百节点以内以限制 Gossip 成本。
    • 节点加入通过 MEET 传播,无需多数派批准。

    但注意: 规模扩大时:

    • Gossip 包体增大
    • 收敛时间增加
    • 网络复杂度上升
  • Raft

    • 推荐 3–7 个节点。
    • 节点数增加会放大:

      • 选举成本
      • 日志复制延迟
      • 提交延迟
    • 成员变更需经一致性协议(joint consensus)。

核心差异:Redis 为扩展性牺牲一致性;Raft 为一致性牺牲扩展性。

5. 通信开销与网络复杂度

  • Redis Cluster

    • 稳态下每节点周期性向少量节点发送心跳。
    • 通信复杂度近似 O(N)。
    • FAIL 广播等极端场景下可能接近 O(N²),但属于短期事件。
    • 单包大小受 slot bitmap 与 gossip 上限控制。
  • Raft

    • Leader 必须与所有 Follower 保持心跳。
    • 稳态 O(N)。
    • 每次写操作都需要多数派往返确认。
    • 延迟与网络稳定性直接影响写入性能。

核心差异:Redis 的开销与“状态传播频率”相关;Raft 的开销与“写入频率”相关。

6. 冲突解决与脑裂机制

  • Redis Cluster

    • 基于 configEpoch 单调递增。
    • 当两个节点声明同一 slot:

      • configEpoch 较大者胜出。
    • 网络分区期间可能出现双主。
    • 分区恢复后通过纪元比较收敛。
    • 不保证分区期间无双写。
  • Raft

    • 基于 term 与日志匹配规则。
    • 任意时刻最多只有一个多数派 Leader。
    • 少数派分区无法提交写。
    • 从机制上避免双主。

核心差异:Redis 解决“最终归属冲突”;Raft 防止“写入冲突发生”。


这部分内容已经涵盖了 Redis Cluster Gossip 协议的绝大多数核心技术细节,希望能帮助您在复习中建立完整的知识体系。

本文由作者按照 CC BY 4.0 进行授权