Gossip简介
2010-08-02 20:14
423 查看
Cassandra集群没有中心节点,各个节点的地位完全相同,它们通过一种叫做gossip的协议维护集群的状态。通过gossip,每个节点都
能知道集群中包含哪些节点,以及这些节点的状态,这使得Cassandra集群中的任何一个节点都可以完成任意key的路由,任意一个节点不可用都不会造
成灾难性的后果。
log(N),这里N表示节点的数量。
gossip有两种形式:anti-entropy和rumor-mongering。
gossip中的每个节点维护一组状态,状态可以用一个key/value对表示,还附带一个版本号,版本号大的为更新的状态。
消息的处理有3种方式,Cassandra采用第三种方式——Push-pull-gossip
关于gossip协议本身更详细的信息可以参考这篇paper
。
Cassandra内部有一个Gossiper,每隔一秒运行一次(在Gossiper.java的start方法中),按照以下规则向其他节点发
送同步消息:
随机取一个当前活着的节点,并向它发送同步请求
向随机一台不可达的机器发送同步请求
如果第一步中所选择的节点不是seed,或者当前活着的节点数少于seed数,则向随意一台seed发送同步请求
第一和第二步好理解,通过第一步可以和当前活着的节点同步状态,以更新本地的状态,通过第二步可以尽早发现不可用的节点重新可用了。
第三步中的第一个条件,如果第一步中的节点不是seed,则向随意一台seed发送同步请求也比较好理解,因为seed理论上总是有较多的节点状态
信息。
第三步中第二个条件则有点难理解,当活着的节点数少于seed时,也需要向随机的seed发送同步消息。其实这里是为了避免出现seed孤岛。
如果没有这个判断,考虑这样一种场景,有4台机器,{A, B, C,
D},并且配置了它们都是seed,如果它们同时启动,可能会出现这样的情形:
A节点起来,发现没有活着的节点,走到第三步,和任意一个种子同步,假设选择了B
B节点和A完成同步,则认为A活着,它将和A同步,由于A是种子,B将不再和其他种子同步
C节点起来,发现没有活着的节点,同样走到第三步,和任意一个种子同步,假设这次选择了D
C节点和D完成同步,认为D活着,则它将和D同步,由于D也是种子,所以C也不再和其他种子同步
这时就形成了两个孤岛,A和B互相同步,C和D之间互相同步,但是{A,B}和{C,D}之间将不再互相同步,它们也就不知道对方的存在了。
加入第二个判断后,A和B同步完,发现只有一个节点活着,但是seed有4个,这时会再和任意一个seed通信,从而打破这个孤岛。
关于这个问题更详细的信息可以参考这里
。
Cassandra的每个节点都有一个实现了IEndPointStateChangeSubscriber接口的订阅者,它负责处理接收到的消
息,该接口包含以下方法:
同时Gossiper本身实现了IEndPointStateChangePublisher,该接口包含register和unregister
两个方法,用于添加和删除订阅者。
gossip通信的状态信息主要有3种:
HeartBeatState
ApplicationState
EndPointState
HeartBeatState由generation和version组成,generation每次启动都会变化,用于区分机器重启前后的状态
ApplicationState用于表示系统的状态,每个对象表示一种状态,比如表示当前load的状态大概是这样:(1.2,
20),含义为版本号为20时该节点的load是1.2
EndPointState封装了一个节点的ApplicationState和HeartBeatState
一个节点自身的状态只能由自己修改,其他节点的状态只能通过同步更新。
一次同步的过程
两个节点之间的一次同步过程可以用下图表示:
这里假设192.168.1.1(源节点)决定和192.168.1.2(目标节点)同步,首先源节点向目标节点发送
GossipDigestSynMessage包,这个包包括本机维护的所有节点的状态信息的最新版本摘要,摘要只包含key和version,不包含具
体的value,这样可以减小同步的带宽消耗。
当目标节点收到GossipDigestSynMessage包时,它需要做两件事:
找出收到的消息中比本地版本新的状态,按照版本号差异大小排序,将这些状态的摘要
放入
GossipDigestAckMessage中
找出本地比源节点版本更新的状态,将这些状态
放入GossipDigestAckMessage中
当GossipDigestAckMessage构建完成后,会被发送给源节点
这里按照版本号差异大小排序的原因是每个Message允许发送的状态数量是有限的(参见Gossip.java中的
MAX_GOSSIP_PACKET_SIZE定义),这样可以保证比较老的状态(版本号差异大的)可以优先得到更新。
源机器接收到GossipDigestAckMessage后,使用发送过来的目标节点更新的状态更新本地的状态,这样源节点就获取到了目标节点上
比自己更新的状态。
同时源节点把包含在GossipDigestAckMessage中摘要对应的状态通过GossipDigestAck2Message发送到目标
服务器,目标服务器更新本地的状态,这样目标服务器也获取到了源节点上比自己更新的状态。
完成这样一次同步后,源节点和目标节点上的状态都得到了同步。这中工作方式还是比较优的
能知道集群中包含哪些节点,以及这些节点的状态,这使得Cassandra集群中的任何一个节点都可以完成任意key的路由,任意一个节点不可用都不会造
成灾难性的后果。
gossip介绍
gossip的学名叫做Anti-entropy(逆熵?),比较适合在没有很高一致性要求的场景中用作同步信息。信息达到同步的时间大概是log(N),这里N表示节点的数量。
gossip有两种形式:anti-entropy和rumor-mongering。
gossip中的每个节点维护一组状态,状态可以用一个key/value对表示,还附带一个版本号,版本号大的为更新的状态。
消息的处理有3种方式,Cassandra采用第三种方式——Push-pull-gossip
Push-gossip | A节点将状态集合发送到B,B通过和本地的状态集合比较,返回 S(A)和S(B)的笛卡尔积 |
Pull-gossip | A发送一个摘要(digest,只包含key和version)给 B,B通过比较,仅仅返回A上需要更新的状态 |
Push-pull-gossip | 这种方式和pull-gossip一样,在B发送给A其需要更新的 状态的同时,会向A请求本地过期的状态 |
。
Cassandra的实现
当一个节点启动时,获取配置文件(storage-conf.xml)中的seeds配置,从而知道集群中所有的seed节点。Cassandra内部有一个Gossiper,每隔一秒运行一次(在Gossiper.java的start方法中),按照以下规则向其他节点发
送同步消息:
随机取一个当前活着的节点,并向它发送同步请求
向随机一台不可达的机器发送同步请求
如果第一步中所选择的节点不是seed,或者当前活着的节点数少于seed数,则向随意一台seed发送同步请求
第一和第二步好理解,通过第一步可以和当前活着的节点同步状态,以更新本地的状态,通过第二步可以尽早发现不可用的节点重新可用了。
第三步中的第一个条件,如果第一步中的节点不是seed,则向随意一台seed发送同步请求也比较好理解,因为seed理论上总是有较多的节点状态
信息。
第三步中第二个条件则有点难理解,当活着的节点数少于seed时,也需要向随机的seed发送同步消息。其实这里是为了避免出现seed孤岛。
如果没有这个判断,考虑这样一种场景,有4台机器,{A, B, C,
D},并且配置了它们都是seed,如果它们同时启动,可能会出现这样的情形:
A节点起来,发现没有活着的节点,走到第三步,和任意一个种子同步,假设选择了B
B节点和A完成同步,则认为A活着,它将和A同步,由于A是种子,B将不再和其他种子同步
C节点起来,发现没有活着的节点,同样走到第三步,和任意一个种子同步,假设这次选择了D
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 | 机器不可用 |
两个方法,用于添加和删除订阅者。
gossip通信的状态信息主要有3种:
HeartBeatState
ApplicationState
EndPointState
HeartBeatState由generation和version组成,generation每次启动都会变化,用于区分机器重启前后的状态
ApplicationState用于表示系统的状态,每个对象表示一种状态,比如表示当前load的状态大概是这样:(1.2,
20),含义为版本号为20时该节点的load是1.2
EndPointState封装了一个节点的ApplicationState和HeartBeatState
一个节点自身的状态只能由自己修改,其他节点的状态只能通过同步更新。
一次同步的过程
两个节点之间的一次同步过程可以用下图表示:
这里假设192.168.1.1(源节点)决定和192.168.1.2(目标节点)同步,首先源节点向目标节点发送
GossipDigestSynMessage包,这个包包括本机维护的所有节点的状态信息的最新版本摘要,摘要只包含key和version,不包含具
体的value,这样可以减小同步的带宽消耗。
当目标节点收到GossipDigestSynMessage包时,它需要做两件事:
找出收到的消息中比本地版本新的状态,按照版本号差异大小排序,将这些状态的摘要
放入
GossipDigestAckMessage中
找出本地比源节点版本更新的状态,将这些状态
放入GossipDigestAckMessage中
当GossipDigestAckMessage构建完成后,会被发送给源节点
这里按照版本号差异大小排序的原因是每个Message允许发送的状态数量是有限的(参见Gossip.java中的
MAX_GOSSIP_PACKET_SIZE定义),这样可以保证比较老的状态(版本号差异大的)可以优先得到更新。
源机器接收到GossipDigestAckMessage后,使用发送过来的目标节点更新的状态更新本地的状态,这样源节点就获取到了目标节点上
比自己更新的状态。
同时源节点把包含在GossipDigestAckMessage中摘要对应的状态通过GossipDigestAck2Message发送到目标
服务器,目标服务器更新本地的状态,这样目标服务器也获取到了源节点上比自己更新的状态。
完成这样一次同步后,源节点和目标节点上的状态都得到了同步。这中工作方式还是比较优的
相关文章推荐
- Struts Gossip: 简介 RequestProcessor
- 最终一致性算法Gossip简介
- Samba-2.2.0版本特色及安装简介
- WCF 基础加开发简介
- android常用类库简介
- XPath盲注简介
- 基础知识——算法复杂度 时间复杂度,空间复杂度简介
- ospf简介及实验
- MongoDB复制集(Replication Sets)简介
- javabean简介
- Python中title()方法的使用简介
- 解压rpm包的linux命令及rpm命令使用简介
- Qt 类简介之 Qt QApplication 类
- Git简介
- 布隆过滤器简介bloomFilter
- PHI 数据库简介
- 模糊控制系统---结构简介
- pragma指令简介
- ssh远程工具下载ssh简介配置
- 威博文件管理系统4.0支持的webdav协议简介