ZooKeeper中实现分布式数据一致性的ZAB协议详解

一、ZAB 协议简介

在深入了解ZooKeeper之前,认为ZooKeeper就是Paxos算法的一个实现。但事实上,ZooKeeper并没有完全采用Paxos算法,而是使用了一种称为ZooKeeper Atomic Broadcast (ZAB, ZooKeeper 原子消息广播协议)的协议作为其数据一致性的核心算法。

ZAB协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的原子广播协议。
ZAB协议并不像Paxos算法那样,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper设计的崩溃可恢复的原子消息广播算法。

在ZooKeeper中,主要依赖ZAB协议来实现分布式数据一致性,基于该协议,ZooKeeper实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性。具体的,ZooKeeper使用一个单一的主进程来接收并处理客户端的所有事务请求,并采用ZAB的原子广播协议,将服务器数据的状态变更以事务Proposal的形式广播到所有的副本进程上去。ZAB协议的这个主备模型架构保证了同一时刻集群中只能够有一个主进程来广播服务器的状态变更,因此能够很好地处理客户端大量的并发请求。另一方面,考虑到在分布式环境中,顺序执行的一些状态变更其前后会存在一定的依赖关系,有些状态变更必须依赖于比它早生成的那些状态变更,例如变更C需要依赖变更A和变更B。这样的依赖关系也对ZAB协议提出了一个要求:ZAB协议必须能够保证一个全局的变更序列被顺序应用,也就是说,ZAB协议需要保证如果一个状态变更已经被处理了,那么所有其依赖的状态变更都应该已经被提前处理掉了。(意思就是已经处理的事务请求不会再处理,不能丢失)最后,考虑到主进程在任何时候都有可能出现崩溃退出或重启现象,因此,ZAB协议还需要做到在当前主进程出现上述异常情况的时候,依旧能够正常工作。

ZAB协议的核心是定义了对于那些会改变ZooKeeper服务器数据状态的事务请求的处理方式,即:

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader 服务器,而余下的其他服务器则成为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal (提议),并将该Proposal 分发给集群中所有的Follower服务器。之后Leader 服务器需要等待所有Follower 服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal 进行提交。

二、协议内容

ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。

什么时候会进行Leader选举?
当整个服务框架在启动过程中,或是当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader 服务器。

什么时候进入消息广播模式 和 数据恢复模式?

当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。
当集群中已经有过半的Follower 服务器完成了和Leader 服务器的状态同步,那么整个服务框架就可以进入消息广播模式了。当一台同样遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到Leader 所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。

当Leader服务器出现崩溃退出或机器重启,亦或是集群中已经不存在过半的服务器与该Leader服务器保持正常通信时,那么在重新开始新一轮的原子广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此达到一个一致的状态,于是整个ZAB流程就会从消息广播模式进入到崩溃恢复模式。
一个机器要成为新的Leader,必须获得过半进程的支持,同时由于每个进程都有可能会崩溃,因此,在ZAB协议运行过程中,前后会出现多个Leader, 并且每个进程也有可能会多次成为Leader.进入崩溃恢复模式后,只要集群中存在过半的服务器能够彼此进行正常通信,那么就可以产生一个新的Leader并再次进人消息广播模式。

只有Leader能处理事务,如果其他节点收到事务请求呢?
ZooKeeper 设计成只允许唯一的一个Leader服务器来进行事务请求的处理。Leader服务器在接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议,而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给
Leader服务器。

消息广播

ZAB协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。针对客户端的事务请求,Leader 服务器会为其生成对应的事务Proposal, 并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交,如图所示就是ZAB协议消息广播流程的示意图。
在这里插入图片描述

ZAB协议中涉及的二阶段提交过程的不同之处?

在ZAB协议的二阶段提交过程中,移除了中断逻辑,所有的Follower 服务器要么正常反馈Leader提出的事务Proposal,要么就抛弃Leader服务器。同时,ZAB协议将二阶段提交中的中断逻辑移除意味着我们可以在过半的Follower 服务器已经反馈Ack之后就开始提交事务Proposal 了,而不需要等待集群中所有的Follower服务器都反馈响应。当然,在这种简化了的二阶段提交模型下,是无法处理Leader 服务器崩溃退出而带来的数据不一致问题的,因此在ZAB协议中添加了另一个模式,即采用崩溃恢复模式来解决这个问题。另外,整个消息广播协议是基于具有FIFO(先进先出)特性的TCP协议来进行网络通信的,因此能够很容易地保证消息广播过程中消息接收与发送的顺序性。

什么是ZXID?

在整个消息广播过程中,Leader服务器会为每个事务请求生成对应的Proposal来进行广播,并且在广播事务Proposal之前,Leader服务器会首先为这个事务Proposal分配一个全局单调递增的唯一ID, 我们称之为事务ID (即ZXID)。

由于ZAB协议需要保证每一个消息严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序与处理。具体的,在消息广播过程中,Leader服务器会为每一个Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal 依次放入这些队列中去,并且根据FIFO策略进行消息发送。每一个Follower服务器在接收到这个事务Proposal 之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给Leader服务器一个Ack响应。当Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交,而每一个 Follower服务器在接收到Commit消息后,也会完成对事务的提交。

崩溃恢复

什么时候进入崩溃恢复模式?

ZAB协议的这个基于原子广播协议的消息广播过程,在正常情况下运行非常良好,但是一旦Leader服务器出现崩溃,或者说由于网络原因导致Leader服务器失去了与过半Follower的联系,那么就会进入崩溃恢复模式。

在ZAB协议中,为了保证程序的正确运行,整个恢复过程结束后需要选举出一个新的Leader服务器。因此,ZAB协议需要一个高效且可靠的Leader选举算法,从而确保能够快速地选举出新的Leader。同时,Leader选举算法不仅仅需要让Leader自己知道其自身已经被选举为Leader,同时还需要让集群中的所有其他机器也能够快速地感知到选举产生的新的Leader服务器。

基本特性
根据上面的内容,我们了解到,ZAB协议规定了如果一个事务Proposal在一台机器上被处理成功,那么应该在所有的机器上都被处理成功,哪怕机器出现故障崩溃。接下来我们看看在崩溃恢复过程中,可能会出现的两个数据不一致性的隐患及针对这些情况ZAB协议所需要保证的特性。

特性一:
ZAB协议需要确保那些已经在Leader服务器.上提交的事务最终被所有服务器都提交。

假设一个事务在Leader服务器上被提交了,并且已经得到过半Follower服务器的Ack反馈,但是在它将Commit消息发送给所有Follower机器之前,Leader服务器挂了
在这里插入图片描述
上图中的消息C2就是一个典型的例子:在集群正常运行过程中的某一个时刻,Leader服务器先后广播了消息PI、P2、C1、P3和C2,其中,当Leader服务器将消息C2(C2是CommitOfProposal2的缩写,即提交事务Proposal2)发出后就立即崩溃退出了。针对这种情况,ZAB协议就需要确保事务Proposal2最终能够在所有的服务器上都被提交成功,否则将出现不一致。

特性二:
ZAB协议需要确保丢弃那些只在Leader服务器上被提出的事务。

如果在崩溃恢复过程中出现一个需要被丢弃的提案,那么在崩溃恢复结束后
需要跳过该事务Proposal,如下图。
在这里插入图片描述

假设初始的Leader 服务器Server1 在提出了一个事务P3之后就崩溃退出了,从而导致集群中的其他服务器都没有收到这个事务Proposal。于是,当Serverl恢复过来再次加入到集群中的时候,ZAB协议需要确保丢弃Proposal3这个事务。

结合上面提到的这两个崩溃恢复过程中需要处理的特殊情况,就决定了ZAB协议必须设计这样一个Leader 选举算法:
能够确保提交已经被Leader 提交的事务Proposal, 同时丢弃已经被跳过的事务Proposal。

针对这个要求,如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。因为每次提交事务ZXID都会自增,更为重要的是,如果让具有最高编号事务Proposal 的机器来成为Leader, 就可以省去Leader 服务器检查Proposal的提交和丢弃工作的这一步操作了。

三、数据同步

完成Leader选举之后,在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,Leader服务器会首先确认事务日志中的所有Proposal是否都已经被集群中过半的机器提交了,即是否完成数据同步。

ZAB协议的数据同步过程

所有正常运行的服务器,要么成为Leader,要么成为Follower并和Leader保持同步。Leader服务器需要确保所有的Follower服务器能够接收到每一条事务Proposal,并且能够正确地将所有已经提交了的事务Proposal应用到内存数据库中去。具体的,Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交。等到Follower服务器将所有其尚未同步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数据库中后,Leader服务器就会将该Follower服务器加入到真正的可用Follower列表中,并开始之后的其他流程。上面讲到的就是正常情况下的数据同步逻辑。

ZAB协议是如何处理那些需要被丟弃的事务Proposal 的。

在ZAB协议的事务编号ZXID设计中,ZXID是一个64位的数字,其中低32位可以看作是一个简单的单调递增的计数器,针对客户端的每一个事务请求,Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作;而高32位则代表了Leader周期epoch的编号,每当选举产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的epoch值,然后再对其进行加1操作,之后就会以此编号作为新的epoch,并将低32位置0来开始生成新的ZXID。ZAB协议中的这一通过epoch编号来区分Leader周期变化的策略,能够有效地避免不同的Leader服务器错误地使用相同的ZXID编号提出不一样的事务Proposal的异常情况,这对于识别在Leader崩溃恢复前后生成的Proposal非常有帮助,大大简化和提升了数据恢复流程。基于这样的策略,当一个包含了上一个Leader 周期中尚未提交过的事务Proposal 的服务器启动时,其肯定无法成为Leader, 原因很简单,因为当前集群中一定包含一个Quorum集合,该集合中的机器一定包含 了更高epoch的事务Proposal, 因此这台机器的事务Proposal肯定不是最高,也就无法成为Leader 了。当这台机器加入到集群中,以Follower角色连接上Leader服务器之后,Leader服务器会根据自己服务器上最后被提交的Proposal 来和Follower服务器的Proposal进行比对,比对的结果当然是Leader 会要求Follower进行一个回退操作一回退到一个确实已经被集群中过半机器提交的最新的事务Proposal。 举个例子来说,在上图中,当Serverl连接上Leader后,Leader 会要求Serverl去除P3。

四、ZAB算法描述

整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以细分为三个阶段,分别是发现(Discovery).同步(Synchronization) 和广播( Broadcast)阶段。组成ZAB协议的每一个分布式进程,会循环地执行这三个阶段,我们将这样一个循环称为一个主进程周期。

阶段一:发现

阶段-.主要就是Leader选举过程,用于在多个分布式进程中选举出主进程,准Leader L和Follower F的工作流程分别如下。

步骤F.1.1 Follower F将自己最后接受的事务Proposal 的epoch值CEPOCH发送给准Leader L。

步骤L.1.1当接收到来 自过半Follower 的epoch值消息后,准Leader L会生成新的epoch值消息给这些过半的Follower。
关于这个epoch值e’,准Leader L会从所有接收到的epoch值消息中选取出最大的epoch值,然后对其进行加1操作,即为e’。

步骤F.1.2当Follower接收到来自准Leader L的新的epoch值消息后,如果其检测到当前的epoch值小于e’,那么就会将当前epoch值赋值为e’,同时向这个准Leader L反馈Ack消息。在这个反馈消息(ACK-E(F.p,hr))中,包含了当前该Follower的最后一个处理的事务,以及该Follower的历史事务Proposal集合。当LeaderL接收到来自过半Follower的确认消息Ack之后,LeaderL就会从这过半服务器中选取出一个FollowerF,并使用其作为初始化事务集合le’。
关于这个FollowerF的选取,对于Quorum中其他任意-一个FollowerF’,F需要满足以下两个条件中的一个:
CEPOCH(F’p) < CEPOCH (F.p) 需要小于最后一个提交事务的epoch值。
(CEPOCH (F’p)= CEPOCH(F.p))&(F’zxid <F.zxid或F’zxid= F.zxid) 需要等于最后一个提交事务的epoch值并且zxid小于Follower f处理过的历史事务Proposal中最后一个事务Proposal 的事务标识ZXID。
至此,ZAB协议完成阶段一的工作流程。.

阶段二:同步

在完成发现流程之后,就进入了同步阶段。在这一阶段中,Leader L和FollowerF的工
作流程分别如下。

步骤L.2.1 Leader L会将e’(最大的epcho值)和Ie’(初始化事务集合)以NEWLEADER(e’,Ie’)消息的形式发送给所有Quorum(认定整个集群是否可用的一种方式)中的Follower。

步骤F.2.1当Follower 接收到来自Leader L的NEWLEADER(e’,le’)消息后,如果Follower发现CEPOCH (F.p) ≠e’, 那么直接进入下一轮循环,因为此时Follower发现自己还在上一轮,或者更上轮,无法参与本轮的同步。如果CEPOCH (F.p)= e’,那么Follower就会执行事务应用操作。最后,Follower 会反馈给Leader,表明自己已经接受并处理了所有L中的事务Proposal。

步骤L.2.2当Leader接收到来自过半Follower 针对NEWLEADER(e’,Ie’)的反馈消息后,就会向所有的Follower发送Commit消息。至此Leader完成阶段二。

步骤F.2.2当Follower收到来自Leader的Commit消息后,就会依次处理并提交所有在Ie’中未处理的事务。至此Follower完成阶段二。

阶段三:广播
完成同步阶段之后, ZAB协议就可以正式开始接收客户端新的事务请求,并进行消息广播流程。

步骤L.3.1 Leader L接收到客户端新的事务请求后,会生成对应的事务Proposal,并根据ZXID的顺序向所有Follower发送提案<e’,<v,z>>,其中epoch(z) = e’

步骤F.3.1Follower根据消息接收的先后次序来处理这些来自Leader的事务Proposal,并将他们追加到hp中去,之后再反馈给Leader。

步骤L.3.1当Leader 接收到来自过半Follower 针对事务Proposal <e’,<v,z>>的Ack消息后,就会发送Commit <e’,<v,z>>消息给所有Follower,要求它们进行事务的提交。
步骤F.3.2当Follower F接收到来自Leader的Commit <e’,<v,z>>消息后,就会开始提交事务Proposal<e’,<v,z>>。 需要注意的是,此时该FollowerF必定已经提交了事务Proposal<v’,z’>
以上就是整个ZAB协议的三个核心工作流程。

五、ZAB 与Paxos算法的联系与区别

ZAB协议并不是Paxos算法的一个典型实现,在说ZAB和Paxos之间的区别之前,我们首先来看下两者的联系。

  • 两者都存在一个类似于Leader进程的角色,由其负责协调多个Follower进程的运行。
  • Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交。
  • 在ZAB协议中,每个Proposal中都包含了一个epoch值,用来代表当前的Leader周期,在Paxos算法中,同样存在这样的一个标识,只是名字变成了Ballot。

在Paxos算法中,一个新选举产生的主进程会进行两个阶段的工作。第一阶段被称为读阶段,在这个阶段中,这个新的主进程会通过和所有其他进程进行通信的方式来收集上一个主进程提出的提案,并将它们提交。第二阶段被称为写阶段,在这个阶段,当前主进程开始提出它自己的提案。在Paxos算法设计的基础上,ZAB协议额外添加了一个同步阶段。在同步阶段之前,ZAB协议也存在一个和Paxos算法中的读阶段非常类似的过程,称为发现(Discovery)阶段。在同步阶段中,新的Leader会确保存在过半的Follower已经提交了之前Leader周期中的所有事务Proposal。 这一同步阶段的引入,能够有效地保证Leader在新的周期中提出事务Proposal之前,所有的进程都已经完成了对之前所有事务Proposal的提交。一旦完成同步阶段后,那么ZAB就会执行和Paxos算法类似的写阶段。总的来讲,ZAB协议和Paxos 算法的本质区别在于,两者的设计目标不太一样。

不同点总结:
ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如ZooKeeper
Paxos算法则是用于构建一一个分布式的一致性状态机系统。

六、运行状态

在ZAB协议的设计中,每-一个进程都有可能处于以下三种状态之一。

  • LOOKING: Leader选举阶段
  • FOLLOWING: Follower 服务器和Leader保持同步状态
  • LEADING:Leader服务器作为主进程领导状态
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值