• Cassandra集群各个节点之间的通讯


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

    一、Gossip算法背景

            Gossip算法如其名,灵感来自办公室八卦,只要一个人八卦一下,在有限的时间内所有的人都会知道该八卦的信息,这种方式也与病毒传播类似,因此Gossip有众多的别名“闲话算法”、“疫情传播算法”、“病毒感染算法”、“谣言传播算法”。但Gossip并不是一个新东西,之前的泛洪查找、路由算法都归属于这个范畴,不同的是Gossip给这类算法提供了明确的语义、具体实施方法及收敛性证明。

    二、Gossip算法特点

            Gossip算法又被称为反熵(Anti-Entropy),熵是物理学上的一个概念,代表杂乱无章,而反熵就是在杂乱无章中寻求一致,这充分说明了Gossip的特点:在一个有界网络中,每个节点都随机地与其他节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致(很神奇)。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,只要这些节点可以通过网络连通,最终他们的状态都是一致的。

    三、Gossip本质

            Gossip是一个带冗余的容错算法,更进一步,Gossip是一个最终一致性算法。虽然无法保证在某个时刻所有节点状态一致,但可以保证在“最终”所有节点一致,“最终”是一个现实中存在,但理论上无法证明的时间点。因此Gossip适合没有很高一致性要求的场景。

            因为Gossip不要求节点知道所有其他节点,因此又具有去中心化的特点,节点之间完全对等,不需要任何的中心节点。实际上Gossip可以用于众多能接受“最终一致性”的领域:失败检测、路由同步、Pub/Sub、动态负载均衡。

            但Gossip的缺点也很明显,冗余通信会对网路带宽、CUP资源造成很大的负载,而这些负载又受限于通信频率,该频率又影响着算法收敛的速度。

    四、Gossip节点的通信方式及收敛性

            Gossip中的每个节点维护一组状态,状态可以用一个key/value对表示,还附带一个版本号,版本号大的状态比版本号小的新。两个节点(A、B)之间存在以下三种通信方式:

    通信方式

    含义

    push

    A节点将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A发过来的数据中比自己新的数据

    pull

    A不发送数据的value,仅发送数据的摘要key和version给B。B根据版本比较数据,将本地比A新的数据(key,value,version)推送给A,A更新自己的本地数据

    push/pull

    与pull类似,A仅发送摘要给B。不同之处在于,B比较版本后,不仅将比A新的数据发送给A,同时还向A请求A的摘要中比自己新的数据

            如果把两个节点数据同步一次定义为一个周期,则在一个周期内,push需通信1次,pull需2次,push/pull则需3次,从效果上来讲,push/pull最好,理论上一个周期内可以使两个节点完全一致。直观上也感觉,push/pull的收敛速度是最快的。

            假设每个节点通信周期都能选择(感染)一个新节点,则Gossip算法退化为一个二分查找过程,每个周期构成一个平衡二叉树,收敛速度为O(n2 ),对应的时间开销则为O(logn )。这也是Gossip理论上最优的收敛速度。但在实际情况中最优收敛速度是很难达到的。

            显然pull的收敛速度大于push,而每个节点在每个周期被感染的概率都是固定的p(0<p<1),因此Gossip算法是基于p的平方收敛,也成为概率收敛,这在众多的一致性算法中是非常独特的。

            Gossip的节点的工作方式又分为以下两种:

            Anti-Entropy(反熵):以固定的概率传播所有的数据

            Rumor-Mongering(谣言传播):仅传播新到达的数据

            Anti-Entropy模式有完全的容错性,但有较大的网络、CPU负载;Rumor-Mongering模式有较小的网络、CPU负载,但必须为数据定义”最新“的边界,并且难以保证完全容错,对失败重启且超过”最新“期限的节点,无法保证最终一致性,或需要引入额外的机制处理不一致性。

    五、cassandra中Gossip的节点同步规则

            当一个节点启动时,获取配置文件中的seeds配置。cassandra作为一个去中心化的分布式系统,没有中心节点的存在。但为了让节点启动时能与集群通信,仍然需要为它配置最少一个seed节点。

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

            1.随机取一个当前活着的节点,并向它发送同步请求

            2.随机取一个不可达的节点,并向它们发送同步请求

            3.如果第一步中所选择的节点不是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中Gossip的实现

            Cassandra采用的通信方式是push/pull,如前文所述,push/pull有三个阶段。在每个阶段,节点之间都需要传递一些自己的状态信息。状态信息的传递是通过封装在一种特定的消息里传递,每个阶段传递的消息格式均不同,如下表:

    消息名

    含义

    GossipDigitsSynMessage

    A向B请求同步

    GossipDigitsAckMessage

    B返回自己拥有的比A新的数据给A

    GossipDigitsAck2Message

    A再返回自己拥有的比B新的数据给B

            Gossip互相之间通信,通过上表的消息封装需要传递的信息。节点间互相交换的状态信息主要有以下3种:

    状态信息

    含义

    HeartBeat

    心跳信息,由generation和version组成。generation每次系统启动都加1,用于区分重启前后的状态

    ApplicationState

    用于表示系统状态,存储系统的负载信息等

    EndPointState

    维护节点自身数据的全局version,并封装HeartBeat和ApplicationState

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

    方法名

    含义

    onjoin

    有机器加入到集群中

    onChange

    有状态发生变更了

    onAlive

    机器可用

    onDead

    机器不可用

            下图所示是两个cassandra节点之间通过gossip进行状态同步的完整过程。

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

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

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

            2.找出本地比源节点版本更新的状态,放入GossipDigestAckMessage中,并将它发送回源节点。

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

            源机器接收到GossipDigestAckMessage后,同样也做两件事:

            1.使用目标节点发送过来的比自己新的状态更新本地的状态,源节点获取到了目标节点上比自己更新的状态。

            2.源节点把包含在GossipDigestAckMessage中,目标节点向自己请求更新的摘要对应的状态信息通过GossipDigestAck2Message发送到目标服务器。

            目标服务器更新本地的状态,这样目标服务器也获取到了源节点上比自己更新的状态。完成这样一次同步后,源节点和目标节点上的状态都得到了同步。

    转载来自:http://blog.csdn.net/fykhlp/article/details/7325091

  • 相关阅读:
    Codeforces Round #260 (Div. 2)
    面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]
    ssh自动输入密码脚本 切换目录脚本
    make工作时的执行步骤
    Codeforces Round #259 (Div. 2)
    Codeforces Round #258 (Div. 2)
    如何在半径为1的圆中随机选取一个点
    面试中常问的有关随机选取k个数的总结
    topcoder SRM 628 DIV2 BracketExpressions
    topcoder SRM 628 DIV2 BishopMove
  • 原文地址:https://www.cnblogs.com/it-deepinmind/p/14549714.html
Copyright © 2020-2023  润新知