什么是 Gossip 协议?

Gossip 协议,中文常译为流言协议或流行病协议,是一种去中心化的、基于感染式传播的通信协议。它的灵感来源于人类社会中的流言传播或流行病扩散。

其核心思想是:

  • 随机选择:集群中的每个节点都会随机地选择几个其他节点。
  • 交换信息:节点之间周期性地(例如,每秒一次)交换自己所知道的信息(即元数据)。
  • 最终一致性:经过一段时间后,集群中的所有节点都会获得完全一致的元数据信息,从而达到最终一致性

这个过程就像办公室里一个人知道了一个八卦,他随机找几个人说了这个八卦,这些人又随机找其他人说,很快整个办公室的人都知道了。


为什么 Redis 集群需要 Gossip 协议?

Redis 集群是一个去中心化的架构。它没有像 ZooKeeper 或 etcd 那样的中心协调节点来统一管理所有节点的状态信息。那么,集群中的每个节点是如何知道其他节点的存在、状态(是在线还是下线)、以及负责的槽位(slot)信息呢?

这就是 Gossip 协议的用武之地。Redis 集群使用 Gossip 协议来实现:

  1. 节点发现:新节点加入集群后,如何让其他节点知道它。
  2. 元数据传播:将每个节点掌握的集群状态信息(如槽位分配情况)传播给所有其他节点。
  3. 故障检测:检测节点是否下线,并最终在整个集群内达成共识,从而触发故障转移(Failover)。

Gossip 协议在 Redis 集群中的具体工作方式

通信端口

每个 Redis 集群节点都需要开启两个 TCP 端口:

  • 客户端通信端口(默认 6379):用于服务客户端请求。
  • 集群总线端口(默认 16379):用于节点间通过 Gossip 协议进行通信。集群总线端口 = 客户端端口 + 10000

节点间所有的 Gossip 通信都通过集群总线进行。

消息类型

Gossip 协议承载了多种消息类型,其中最主要的是 PINGPONG 消息。它们本质上是相同的,PONG 是对 PING 的回复,有时也用于主动推送信息。

  • PING 消息:是信息交换的载体。一个 PING 消息不仅用于探测节点是否存活,更重要的是它携带了丰富的元数据
  • PONG 消息:用于回复 PING 消息,内容同样包含元数据。也可以用于在收到 MEET 消息后(新节点加入)立即确认。

消息内容

一个 PING/PONG 消息包主要包含两个部分:

  1. Header(头部):包含发送节点自身的一些信息,如:

    • 节点 ID
    • 当前节点的纪元(配置纪元,用于故障转移和槽位信息版本控制)
    • 节点角色(主节点/从节点)
    • 节点 IP 和端口
    • 节点状态(如是否被标记为下线)
    • 负责的槽位位图(如果它是主节点)
  2. Gossip 节(Gossip Section):这是协议的精髓。它包含了发送节点所知道的关于其他节点的一些信息(通常是随机挑选的几个节点)。对于每个被提到的节点,信息包括:

    • 该节点的 ID
    • 该节点的 IP 和端口
    • 该节点的状态标志(如:PFAILFAIL

工作流程

  1. 周期性随机选择
    每个节点默认每秒会执行 10 次以下操作,但只会选择 1 个节点进行通信(cluster-node-time 可配置)。它从自己已知的节点列表中(包括正常、疑似下线的节点,但不包括已确认下线的节点),随机选择 1 个节点发送 PING 消息。

    • 为什么是随机? 随机性避免了所有节点同时向同一个节点发起通信,导致网络拥塞。它是一种负载均衡。
    • 为什么选择 1 个? 这是一个权衡。太频繁会增加网络负担,太慢则会导致信息收敛(最终一致)得太慢。
  2. 信息交换与更新

    • 节点 A 向随机选择的节点 B 发送一个 PING 消息。这个消息里包含了 A 自己的头部信息,以及 A 所知道的关于其他随机几个节点的 Gossip 信息。
    • 节点 B 收到 PING 后,会回复一个 PONG 消息,其中同样包含 B 自己的头部信息和它知道的 Gossip 信息。
    • 节点 A 收到 PONG 后,会解析其中的信息,并与自己本地存储的信息进行对比和更新。例如:
      • 如果发现了一个自己不知道的新节点,它会把这个新节点加入到自己的节点列表中。
      • 如果发现某个节点的状态(如槽位分配)比自己知道的更新(通过配置纪元判断),它会更新本地的信息。
      • 如果收到关于其他节点故障的信息,也会记录下来。
  3. 故障检测(Fail Detection)
    这是 Gossip 协议一个非常重要的应用。

    • PFAIL(可能下线):如果节点 A 在 cluster-node-timeout 时间内无法与节点 B 成功通信,它会在本地将节点 B 标记为 PFAIL(Possible Failure,可能下线)。
    • 传播 PFAIL 状态:之后,当节点 A 向其他节点发送 PING 消息时,它携带的 Gossip 信息里就会包含节点B是PFAIL的状态。
    • FAIL(确认下线):集群中的每个节点都会收到来自其他节点关于节点 B 的 Gossip 信息。如果大多数主节点都认为节点 B 是 PFAIL 状态,那么其中一个主节点就会将节点 B 的状态升级为 FAIL,并将这个 FAIL 状态通过 Gossip 协议广播出去。
    • 触发故障转移:一旦某个主节点被确认为 FAIL 状态,并且它有一个从节点,那么就会触发故障转移流程,由该从节点晋升为新的主节点。

优点与缺点

优点:

  • 去中心化与高可用:无需依赖任何中心节点,任何节点的下线都不会影响元数据的传播(只要集群大部分主节点存活)。
  • 可扩展性:新节点的加入或信息的传播压力会均匀地分散到所有节点上,集群规模越大,每个节点需要通信的邻居数并不需要线性增长,因此扩展性良好。
  • 容错性:由于随机选择和对等通信,它对网络抖动和节点故障具有很高的抵抗力。信息可以通过多条路径传播,最终总能到达所有节点。
  • 最终一致性:虽然信息传播有延迟,但能保证所有节点最终会拥有一致的集群视图。

缺点:

  • 消息延迟:信息到达所有节点需要一定的时间(通常以秒计),是最终一致而非强一致。在信息收敛期间,不同节点可能看到不同的集群状态。
  • 消息冗余:由于是随机传播,同一条消息可能会被多次发送到同一个节点,造成一定的网络带宽浪费。

总结

Redis 集群的 Gossip 协议就像集群的神经系统,它通过每个节点随机地交换信息,让所有节点最终都能感知到整个集群的完整状态(节点列表、槽位映射、故障状态)。这种设计完美契合了 Redis 集群去中心化的架构思想,以其简单、可靠、高容错的特性,实现了集群的自我管理和自我修复。