Gossip算法及其在Redis集群里的运用

Gossip的一些特点
在一个有界网络中,每个节点都随机地与其它节点通信,经过一段无序的通信后,最终所有节点的状态都会达成一致,即使是有节点宕机后重启或有新节点加入,但一段时间后这些节点的状态也会与其它节点达成一致,从这一点来说,Gossip天然具有分布式容错的优点。
根据上面的描述,我们知道 Gossip是一个最终一致性算法,它无法保证在某个时刻所有节点状态一致,但可以保证最终一致。另外Gossip不要求节点知道所有其它节点,因此又具有去中心化的特点,节点之间完全对等,不需要任何的中心节点。
但Gossip的缺点也很明显,冗余通信会对网络带宽与CPU资源造成很大的负载,从有的资料上看到说当集群规模超过百节点级别后,Gossip 协议的效率将会显著下降,通信成本越来越高。
Gossip节点的三种通信方式
两个节点(A,B)之间存在三种通信方式:

  • push:A节点将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A中比自己新的数据
  • pull:A仅将数据(key,version)推送给B,B将本地比A新的数据(key,value,version)推送给A,A更新本地
  • push/pull:与pull类似,只是多了一步,A再将本地比B新的数据推送给B,B更新本地
如果将两个节点数据同步一次定义为一个周期,则在一个周期内,push需通信1次,pull需2次,push/pull则需3次。但从效果上来讲,push/pull最好,理论上一个周期可以使两个节点完全一致。
Gossip在Redis Cluster中的作用
在分布式系统中,需要提供维护节点元数据信息的机制,所谓元数据是指节点负责哪些数据、主从属性、是否故障等状态信息。常见的元数据维护方式一般分集中式与无中心式。Redis Cluster采用Gossip协议实现无中心式。
详细来讲,Redis Cluster中使用Gossip主要有两大作用:
  1. 去中心化,以实现分布式和弹性扩展
  2. 失败检测,以实现高可用
【Gossip算法及其在Redis集群里的运用】节点通信基础
Redis Cluster中的每个实例监听两个TCP端口:6379用于服务客户端查询;16379用于集群内部通信。集群中节点通信方式如下:
  1. 每个节点在固定周期内通过特定规则选择几个节点发送Ping消息
  2. 接收到Ping消息的节点用Pong消息作为回应
集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,只要这些节点彼此可以正常通信,最终它们会达到一致的状态。当节点故障、新节点加入、主从关系变化,槽信息变更等事件发生时,通过不断的Ping/Pong消息通信,经过一段时间后所有的节点都会知道集群全部节点的最新状态,从而达到集群状态同步的目的。
Gossip在Redis Cluster里的消息种类
  • Meet消息:用于通知新节点加入。消息发送者通知接收者加入到当前集群,Meet消息通信正常完成后,接收节点会加入到集群中并进行周期性的Ping,Pong消息交换
  • Ping消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其它节点发送Ping消息,用于检测节点是否在线和交换彼此状态信息。Ping消息发送封装了自身节点和部分其它节点的状态数据
  • Pong消息:当接收到Ping,Meet消息时,作为响应消息回复给发送方确认消息正常通信。Pong消息内部封装了自身状态数据。节点也可以向集群内广播自身的Pong消息来通知整个集群对自身状态进行更新
  • Fail消息:当节点判集群内另一节点下线时,会向集群内广播一个Fail消息,其他节点接收到Fail消息之后会将对应节点更新为下线状态
由于集群内部需要频繁地进行节点信息交换,而Ping/Pong消息携带当前节点和部分其它节点的状态数据,会加重带宽和计算的负担。Redis集群内节点通信采用固定频率(定时任务每秒执行10次),因此,节点每次选择需要通信的节点列表非常重要。通信节点选择过多虽然可以做到信息及时交换但成本过高。节点选择过少则会降低集群内所有节点彼此信息交换的频率,从而影响故障判定、新节点发现等需求的速度。因此Redis集群的Gossip协议需要兼顾信息交换实时性和成本开销。
最后,redis集群的故障检测,故障转移与master选举可以看之前的文章:redis学习之集群
参考的文章:Gossip算法 Gossip算法
分布式一致性协议 Gossip 和 Redis 集群原理解析

    推荐阅读