ZooKeeper的原子广播(ZAB协议)

来源:互联网 发布:golang cgo windows 编辑:程序博客网 时间:2024/06/07 21:51
    ZooKeeper作为高可用的一致性协调框架,自然有着一致性算法的实现,ZooKeeper使用的是ZAB协议作为数据一致性的算法,即ZAB(ZooKeeper Atomic Broadcast )。ZAB在Paxos算法上做了重要改造,和Paxos有着明显的不同。为讨论ZAB,我们假定ZooKeeper已经开启仲裁模式(ZooKeeper还有一种独立模式,除实验需求以外不要开,因为它无法避免脑裂)。

    ZooKeeper使用的消息系统提供了以下特殊保证:
  • 可靠传输:如果消息m被一台服务器送达,它最终会被送达到所有服务器
  • 全序:如果一台服务器上消息a在消息b前送达,那么在所有服务器上a将比b先送达。如果a和b是已传输过的消息,那么要么a在b前送达,要么b在a前送达(即不可能有同时发生的情况)
  • 因果序:如果一个发送者在消息a送达后再发送消息b,那么a必须排在b之前。如果发送者在送达b后再发送消息c,那么c必须排在b之后

    ZooKeeper的消息系统还需要是高效、可靠、容易实现和维护的。因为大量使用了消息,消息系统还必须满足每秒处理数千个请求的要求。开发者提出的协议假定可以在服务器间建立点对点的FIFO通道。使用TCP协议可以非常简单地实现这一假定,因为TCP具有以下属性:
  • 有序传输:数据发出和数据送达的顺序严格一致,即消息m被送达当且仅当m前发送的所有消息都已被送达。(推论:如果m丢失,m后的所有消息必须丢弃)
  • 关闭后没有消息:一旦FIFO通道关闭,不会再从它收到消息。

    FLP定理(以三个作者名字的首字母命名,nosql一文中讲过)已经证明了在异步的分布式系统中如果容忍错误则不可能达到一致性。为了在允许错误的条件下达到一致性ZooKeeper的开发者使用了超时时间。如果超时计时器出了问题则整个消息系统可能hang住,但不会违背一致性保证。

    ZooKeeper消息协议中有以下元素:
  • 包:通过FIFO通道传送的byte序列
  • 提案:一种共同协议。协议需要法定人数的ZooKeeper服务器来通过。大部分提案可以包含消息,但是像NEW_LEADER这样的提案是不包含消息的
  • 消息:原子地广播到所有ZooKeeper服务器的byte序列
    前面提到,ZooKeeper保证消息的全序,同时它也保证提案的全序。ZooKeeper使用一种ZooKeeper事务id(zxid)来暴露整体顺序。所有提案在提出时均会被打上一个zxid标记,它代表了提案的整体顺序。zxid由两部分组成:周期(epoch)和计数器(counter)。在当前的实现中zxid是一个64位整数,高32位为epoch,低32位为counter,因此zxid也可以记为一个整数对(epoch, count)。epoch的值代表leader的改变,每当选举产生一个新的leader就会生成一个它独有的epoch编号。ZooKeeper使用了一种简单的算法将一个唯一的zxid赋给提案:leader对每个提案只是简单地递增zxid以得到一个唯一的zxid值。Leader激活算法会保证只有一个leader使用一个特定的epoch,因此这个简单的算法可以保证每个提案都有一个唯一的id。
    提案被发送到所有的ZooKeeper服务器,在它们中的法定人数认可该提案后其被提交。如果该提案还包含一个消息,那么在提案被提交时消息被送达。“认可”意味着服务器已将提案保存到持久化存储上。“法定人数”代表一组服务器,必须满足任意两个法定人数对之间至少有一个共同的服务器。因此典型情况下,任意一个法定人数至少有(n/2+1)台服务器即可满足要求,这里n是ZooKeeper服务中的总服务器数。

    ZooKeeper消息协议包含两个阶段:
  • Leader激活:在这个阶段,leader建立起正确的状态并准备发起提案
  • 消息激活:在这个阶段,leader接受消息以发起或协调消息传输
    ZooKeeper消息协议是一种整体协议。它不针对个别提案,而是将提案的流看作一个整体。严格的全序允许我们有效地实现并大大简化了协议。Leader激活中包含了这种整体性的概念,一个leader被激活当且仅当followers中的法定人数(leader自身也算作一个follower)与该leader达成同步,即它们有相同的状态。这个状态包含leader认为所有已提交的提案,以及让followers跟随本leader的提案(NEW_LEADER提案)。


Leader激活

    Leader激活阶段包含了leader选举。在我们当前使用的版本(3.5)中有两种leader选举算法:LeaderElection和FastLeaderElection(AuthFastLeaderElection是FastLeaderElection的变种,使用了UDP和简单的认证机制)。ZooKeeper消息机制不关心具体的leader选举方法,只需要能确保以下两点:
  • leader已经获知所有followers的最大的zxid
  • 一个服务器间的法定人数已经确认会跟随leader
    这两点需求中,只有第一点需要永久保证,因为它是一切正确操作的基础。第二点,即服务器间形成法定人数,只需要大概率下保证即可。ZooKeeper代码会经常检验第二点需求,如果在leader选举期间或之后发生故障导致不足法定人数,会通过终止此次leader激活来恢复状态,然后再次选举。
    Leader选举完成后一台服务器会被指定为leader并等待followers的连接,其他服务器尝试连接到leader。Leader将和followers同步,通过发送followers缺失的提案(DIFF),但如果followers缺失太多提案,将发送一个完整的快照(SNAP)。
    一种特殊情况是,一个follower有一个未被leader获知的提案U。根据因果序可知,提案是按顺序被获知的,因此提案U必有一个比leader已知的zxid更大的zxid。可证明该follower一定是leader选举后才出现的,否则,因其获知了一个更大的zxid,该follower才应该被选举为leader,矛盾。因为已提交的提案必须被法定人数的服务器一致获知,而选出leader的法定人数并未获知提案U,所以提案U必然还未提交,因此它可被丢弃。于是当该follower连接到leader时,leader会通知该follower丢弃提案U(TRUNC)。
    新leader通过获知的最大zxid来确定新的zxid,如前最大zxid的epoch位是e,则leader使用(e+1, 0)作为新的zxid。在leader和follower同步后,leader会发出一个NEW_LEADER提案。一旦NEW_LEADER提案被提交,leader就算完全激活并开始收发其他提案。

    总之,在leader激活阶段有以下规则:
  • follower在和leader同步后将ACK NEW_LEADER提案
  • follower只能ACK一个有特定zxid的NEW_LEADER提案
  • 当一个法定人数的followers全部ACK该提案后,leader将COMMIT该NEW_LEADER提案
  • 当NEW_LEADER提案COMMIT后,follower会COMMIT所有从leader收到的状态
  • 新的leader在NEW_LEADER提案被COMMIT前不会接收任何新的提案
  • 如果leader选举非正常结束,NEW_LEADER提案不会被COMMIT,因为此时该leader相应的法定人数还未形成,因此不会导致问题


消息激活

    完成leader激活后,该leader就可以开始收发提案了。在它仍然是leader的时期,其他leader是无法加入的,因为它达不到必须的followers法定人数个数。如果当前leader失去了必须的法定人数,新leader才会加入,此时它会在leader激活阶段清理不该被接受的提案。
    ZooKeeper消息系统很像经典的两阶段提交,以下是官网的图:

    因为图中所有信道都是FIFO的,所以一切都是有序的。本阶段有以下规则或限制:
  • leader以相同的顺序向所有followers发送提案,且这一顺序和收到请求的顺序保持一致。因为使用了FIFO通道,于是保证followers也按此顺序收到提案。
  • followers以收到提案的顺序处理消息。这意味着消息将被有序地ACK且leader按此顺序收到ACK,仍然是由FIFO通道保证。这也意味着如果某提案上的消息m被写到非易失性存储(硬盘)上,所有在m前提出的提案上的消息也已被写到非易失性存储上。
  • 当法定人数的followers全部ACK某消息后,leader会发出一个COMMIT提案。因为所有消息是按序ACK的,leader发出COMMIT且followers收到该提案也是按序的。
  • COMMIT按序被处理。提案被提交后意味着followers可以分发提案上的消息了(发送给客户端)。


与Paxos对比

    ZAB是Multi Paxos吗?否,因为Multi Paxos要求以某种方式保证只有一个协调者。而ZAB不需要依赖于这样的保证,借助leader激活阶段,leader变化或旧leader仍然有效的情况都能很好处理。
    ZAB是Paxos(Basic Paxos)吗?消息激活阶段是Paxos的第二阶段吗?否,实际上消息激活和两阶段提交很像,但不需要处理取消操作。消息激活本质上和两者的区别是,它需要保证在所有提案间有序。如果不能保证所有包之间严格的FIFO顺序,那就不是ZAB了(Paxos或两阶段提交都不需要)。另外leader激活阶段也和两者有区别,比如epoch的使用使得我们可以跳过未提交的提案,且不必担心某zxid的提案被提交两次。


法定人数

    最后澄清一下不那么“典型”的法定人数情况。原子广播和leader选举都使用了法定人数的概念以保证系统的一致性。比如在leader选举提案中:当收到法定人数的服务器的认可后,leader能且仅能提交一次该提案。
    最简单的法定人数是相对多数,即前面给出的(n/2 +1)。前面提到过法定人数的一个重要属性是,如果一个法定人数解散,另一个法定人数形成,两次之间至少要有一台服务器交集,相对多数显然符合这一要求。
    但除了相对多数外,也有其他的构成法定人数的方法。比如可以对每台服务器分配一个投票的权重,代表某些服务器在投票时更重要。为达成一个法定人数,只需要加权的总票数大于所有服务器加权票数和的1/2即可。
    另一种方法是分层法,这种方法也使用权重,并且很适合多地部署的ZooKeeper集群。使用这种方法时,我们将服务器分组成不相交的集合,并设定每台的权重,只要多数的组中的服务器加权投票形成多数,即可构成法定人数。例如有三个组,每个组中有三台服务器,每台的权重都设为1,我们只需要至少有两个组中的加权投票数达到2即可,于是最少只需要4台服务器来构成法定人数。如果有G个组,构成法定人数需要G’个组,满足G’>G/2,且对这G’个组中每组的服务器集合g,需要有一个集合g’,满足g’中所有机器的加权和W’大于g中所有机器的加权和W的一半,即W’>W/2。



2 0
原创粉丝点击