Gossip简介

Cassandra集群没有中心节点,各个节点的地位完全相同,它们通过一种叫做gossip的协议维护集群的状态。通过gossip,每个节点都 能知道集群中包含哪些节点,以及这些节点的状态,这使得Cassandra集群中的任何一个节点都可以完成任意key的路由,任意一个节点不可用都不会造 成灾难性的后果。

gossip介绍

gossip的学名叫做Anti-entropy(逆熵?),比较适合在没有很高一致性要求的场景中用作同步信息。信息达到同步的时间大概是 log(N),这里N表示节点的数量。

gossip有两种形式:anti-entropy和rumor-mongering。

gossip中的每个节点维护一组状态,状态可以用一个key/value对表示,还附带一个版本号,版本号大的为更新的状态。

消息的处理有3种方式,Cassandra采用第三种方式——Push-pull-gossip

Push-gossipA节点将状态集合发送到B,B通过和本地的状态集合比较,返回 S(A)和S(B)的笛卡尔积
Pull-gossipA发送一个摘要(digest,只包含key和version)给 B,B通过比较,仅仅返回A上需要更新的状态
Push-pull-gossip这种方式和pull-gossip一样,在B发送给A其需要更新的 状态的同时,会向A请求本地过期的状态

关于gossip协议本身更详细的信息可以参考这篇paper

Cassandra的实现

当一个节点启动时,获取配置文件(storage-conf.xml)中的seeds配置,从而知道集群中所有的seed节点。

Cassandra内部有一个Gossiper,每隔一秒运行一次(在Gossiper.java的start方法中),按照以下规则向其他节点发 送同步消息:

  1. 随机取一个当前活着的节点,并向它发送同步请求
  2. 向随机一台不可达的机器发送同步请求
  3. 如果第一步中所选择的节点不是seed,或者当前活着的节点数少于seed数,则向随意一台seed发送同步请求

第一和第二步好理解,通过第一步可以和当前活着的节点同步状态,以更新本地的状态,通过第二步可以尽早发现不可用的节点重新可用了。

第三步中的第一个条件,如果第一步中的节点不是seed,则向随意一台seed发送同步请求也比较好理解,因为seed理论上总是有较多的节点状态 信息。

第三步中第二个条件则有点难理解,当活着的节点数少于seed时,也需要向随机的seed发送同步消息。其实这里是为了避免出现seed孤岛。

如果没有这个判断,考虑这样一种场景,有4台机器,{A, B, C, D},并且配置了它们都是seed,如果它们同时启动,可能会出现这样的情形:

  1. A节点起来,发现没有活着的节点,走到第三步,和任意一个种子同步,假设选择了B
  2. B节点和A完成同步,则认为A活着,它将和A同步,由于A是种子,B将不再和其他种子同步
  3. C节点起来,发现没有活着的节点,同样走到第三步,和任意一个种子同步,假设这次选择了D
  4. C节点和D完成同步,认为D活着,则它将和D同步,由于D也是种子,所以C也不再和其他种子同步

这时就形成了两个孤岛,A和B互相同步,C和D之间互相同步,但是{A,B}和{C,D}之间将不再互相同步,它们也就不知道对方的存在了。

加入第二个判断后,A和B同步完,发现只有一个节点活着,但是seed有4个,这时会再和任意一个seed通信,从而打破这个孤岛。

关于这个问题更详细的信息可以参考这里

Cassandra的每个节点都有一个实现了IEndPointStateChangeSubscriber接口的订阅者,它负责处理接收到的消 息,该接口包含以下方法:

方法名含义
onjoin有机器加入到集群中
onChange有状态发生变更了
onAlive机器可用
onDead机器不可用

同时Gossiper本身实现了IEndPointStateChangePublisher,该接口包含register和unregister 两个方法,用于添加和删除订阅者。

gossip通信的状态信息主要有3种:

  1. HeartBeatState
  2. ApplicationState
  3. EndPointState

HeartBeatState由generation和version组成,generation每次启动都会变化,用于区分机器重启前后的状态

ApplicationState用于表示系统的状态,每个对象表示一种状态,比如表示当前load的状态大概是这样:(1.2, 20),含义为版本号为20时该节点的load是1.2

EndPointState封装了一个节点的ApplicationState和HeartBeatState

一个节点自身的状态只能由自己修改,其他节点的状态只能通过同步更新。

一次同步的过程

两个节点之间的一次同步过程可以用下图表示:

gossip

这里假设192.168.1.1(源节点)决定和192.168.1.2(目标节点)同步,首先源节点向目标节点发送 GossipDigestSynMessage包,这个包包括本机维护的所有节点的状态信息的最新版本摘要,摘要只包含key和version,不包含具 体的value,这样可以减小同步的带宽消耗。

当目标节点收到GossipDigestSynMessage包时,它需要做两件事:

  1. 找出收到的消息中比本地版本新的状态,按照版本号差异大小排序,将这些状态的摘要 放入 GossipDigestAckMessage中
  2. 找出本地比源节点版本更新的状态,将这些状态 放入GossipDigestAckMessage中

当GossipDigestAckMessage构建完成后,会被发送给源节点

这里按照版本号差异大小排序的原因是每个Message允许发送的状态数量是有限的(参见Gossip.java中的 MAX_GOSSIP_PACKET_SIZE定义),这样可以保证比较老的状态(版本号差异大的)可以优先得到更新。

源机器接收到GossipDigestAckMessage后,使用发送过来的目标节点更新的状态更新本地的状态,这样源节点就获取到了目标节点上 比自己更新的状态。

同时源节点把包含在GossipDigestAckMessage中摘要对应的状态通过GossipDigestAck2Message发送到目标 服务器,目标服务器更新本地的状态,这样目标服务器也获取到了源节点上比自己更新的状态。

完成这样一次同步后,源节点和目标节点上的状态都得到了同步。这中工作方式还是比较优的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值