- ZooKeeper的ZAB协议
- ZAB协议的两种基本模式:崩溃恢复和消息广播
- 消息广播
- 崩溃回复
- ZAB协议的三个阶段:发现,同步和广播
- 阶段一:发现
- 阶段二:同步
- 阶段三:广播
- ZAB和Paxos算法的联系和区别
- 联系
- 区别
ZooKeeper并不是Paxos算法的实现,ZooKeeper并没有完全采用Paxos算法,而是专门设计的崩溃可恢复的原子消息广播算法ZAB(ZooKeeper Atomic Broadcast)。
ZooKeeper使用了一个单一的主线程来接收并处理客户端的所有事物请求,并采用ZAB协议,将服务器状态变更以事务Proposal的形式广播到所有的副本进程上去。ZAB保证同一时刻一个集群只有一个主线程来广播服务器的状态变更;ZAB协议保证全局的变更会被顺序处理。ZAB协议保证当主线程出现崩溃或者重启现象时,依然能够正常工作。
ZAB协议定义了那些会改变ZooKeeper服务器状态的事务请求的处理方式:
所有事务请求必须由一个全局唯一的服务器来协调处理,这个服务器被称为Leader服务器,而余下的其他服务器则称为Follower服务器。Leader服务器负责将一个客户端的事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器,之后Leader服务器需要等待所有的Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求将其前一个Proposal进行提交。
集群启动时或者Leader服务器出现故障(宕机,重启,或是网络分区导致Leader服务器不能与过半服务器维持正常通信)时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。当选举出新的Leader服务器并有过半的机器的该Leader服务器完成状态同步(也就是数据同步)后,ZAB协议就会退出恢复模式。整个服务框架进入消息广播模式,在消息广播模式下,如果有新的遵守ZAB协议的服务器加入集群,那么这个新加入的机器就自动进入数据恢复模式:找到Leader,与其进行数据同步,之后加入消息广播流程。
ZooKeeper仅允许唯一的一个Leader服务器进行事务请求的处理:Leader服务器接到客户端的事务请求后就生成对应的事务提案并发起一轮广播协议,非Leader服务器收到事务请求时则会将这个请求转发给Leader服务器。
在投票时,Leader自己也是有投票权的,即由一个Leader,两个Follower组成的集群,一个Follower挂掉并不会导致服务中断,因为现在Leader服务器依然有2个(一个Follower一个自己)过半的机器支持。
消息广播协议基于具有FIFO特性的TCP协议来进行网络通信,因此很容易保证消息广播过程中消息接收和发送的顺序性。
在整个消息广播过程中,Leader服务器会为每个事务请求生成对应的Proposal,并在广播事务Proposal之前,为这个Proposal分配一个全局单调递增的唯一ID,我们称之为事务ID(即ZXID)。ZAB协议保证每一个事务Proposal都会严格的按照ZXID的先后顺序来进行排序与处理。
ZAB的消息广播协议类似于2PC,不同的ZAB协议的二阶段提交过程中移除了中断逻辑。Follower服务器要么正常反馈Leader提出的事务Proposal,要么就抛弃。Leader收到过半Follower反馈ACK后就开始提交事务。具体的方式为:Leader为每一个Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送。Follower服务器接收到这个事务Proposal之后,先将其以事务日志的形式写入到本地磁盘,成功写入后反馈给Leader服务器一个Ack响应。Leader服务器收到半数Follower的Ack响应后,就会广播一个Commit消息给所有的Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交,Follower服务器在接收到Commit消息后,也会完成对事务的提交。这种模式虽然不需要等待所有的Follower响应,但是存在Leader服务器崩溃退出带来的数据不一致问题。ZAB协议采用崩溃恢复模式来解决这个问题。
崩溃恢复模式需要选举出一个新的Leader服务器,而Leader选举服务器不仅仅需要高效快速,而且能够让Leader本身已经集群中的其他机器也能快速感知到新的Leader服务器。除此之外,还要保证Leader崩溃不会导致数据不一致,Leader选举完成后能够进行数据同步。
ZAB协议规定如果一个事务Proposal在一台机器上被处理成功,那么应该在所有机器上都被处理成功,哪怕出现故障崩溃,故而ZAB协议需要解决以下导致两个数据不一致的隐患:
- ZAB协议要保证在Leader服务器提交的事务最终被所有服务器提交。如果一个事务在Leader服务器提交,并且已经得到半数的Ack,但是在他将Commit消息发送给所有Follower之前,Leader服务器崩溃。
- ZAB协议要保证丢弃那些只在Leader服务器被提出的事务。即旧Leader在提出一个事务后,发送之前崩溃,其他服务器并没有收到这个事务Proposal。那么等到这个旧Leader恢复后以Follower的身份重新加入集群时,能够确保丢弃这个失效的事务。
为了解决这两种隐患,ZAB协议要求新被选出来的Leader服务器必须拥有集群中最高编号(即ZXID最大)的事务。这样就能保证新选举出来的Leader一定具有所有已经提出的提案,也可以省去新Leader服务器检查Proposal的提交和丢弃的工作。
因此Leader选举完成后,做的第一件事是确认自身事务日志中的所有Proposal是否已经被过半机器提交了。对于未完成同步的Follower,Leader会将未同步的事务以Proposal消息的形式逐个放入Follower对应的队列中发送给Follower,并在每一个Proposal消息后面紧跟一个Commit消息进行事务提交。等到Follower将未同步的事务同步完成,Leader再将该Follower加入到真正可用的Follower列表中开启后续流程。这就解决了问题1。
以上是正常的数据同步,而对于问题2,即那些需要被丢弃的事务提议,ZAB将事务编号的ZXID设计成一个64位的数字,低32位单调递增,为每一个事务Proposal生成ZXID,高32位则调拨Leader周期epoch的编号,每当选举产生新的Leader,新Leader会从自身已提交的最大事务Proposal的ZXID中提取出旧epoch,并将其进行+1作为新的epoch,然后将低32位置0以生成新的ZXID。在这样的策略下,如果一个包含旧Leader周期未提交事务的机器(即上面提到的旧Leader生成事务消息后发送事务消息前崩溃,然后恢复之后加入),首先第一时间肯定不会成为Leader,因为新的Leader及其Follower在epoch+1的情况下肯定拥有更高的ZXID,而这个机器以Follower身份加入集群时,新Leader根据自己已提交的Proposal来和这个机器进行比对,比对发现旧Leader周期未提交的事务,Leader会要求这个Follower进行一个回退–即回退到一个确实已经被机器中过半机器提交的最新事务Proposal。
整个ZAB协议主要包含消息广播和崩溃恢复两个过程,进一步可分为三个阶段:发现(Discovery),同步(Synchronization),广播(Broadcast)。
阶段一:发现阶段一主要是Leader的选举过程,用于多个分布式进程中选举出主进程。准Leader和Follower的工作流程如下:
- Follower将自己最后接受的事务的epoch值发给准Leader
- 准Leader收到过半Follower发送的epoch后,会从所有消息中选取最大的epoch,然后对其加1,生成newepoch,并将这个newepoch发送给那些发送他oldepoch的过半Follower。
- Follower收到准Leader的newepoch后,如果自身的epoch小于newepoch,就会将自身的epoch更新成newepoch,然后向准Leader反馈ACK消息,这个消息包含自身原来的epoch以及自身的历史事务集合FP。准Leader接收到超过半数的Follower对自己发出的newepoch消息的ACK后,就成为了新周期的Leader。
准Leader接受到过半数Follower的ACK消息后,会从中选出epoch最大,且包含最新事务的那个事务集合FP作为初始化事务集合。
阶段二:同步发现阶段完成后,进入同步阶段,Leader和Follower的工作流程如下:
- Leader将newepoch以及选中的初始化事务集合发送给所有Follower。
- Follower接到来自Leader的newepoch以及初始化事务集合后,检查自身的epoch是否等于newepoch,如果不等于,说明自己还未处于最新的一轮,上一轮甚至更前的同步可能还没完成,那么就放弃本轮,进入下一轮循环。如果相等,则根据初始事务集合与自身对比进行初始化,初始化完成则反馈Leader初始化完成。
- Leader收到过半Follower初始化完成的消息后,就向所有Follower发送Commit消息,至此Leader完成二阶段
- Follower收到来自Leader的Commit消息后,会依次提交所有初始化集合中未处理的事务,至此Follower完成二阶段。
同步阶段完成后,ZAB协议就可以正式接收客户端新的事务请求,并进行消息广播。
- Leader收到客户端的消息后,会生成新的事务Proposal,并根据ZXID的顺序向所有Follower发送提案。
- Follower根据消息接收的先后顺序来处理来自Leader的事务,并将他们追加到自己已处理的事务集合中去,之后反馈Leader。
- Leader收到过半Follower的ACK后,发送Commit消息给所有的Follower,要求他们进行事务提交。
- Follower接收到来自Leader的Commit消息后,对事务进行提交。注意此时该Follower一定完成了上一个事务的提交。
以上就是ZAB协议的三个核心工作流程,正常运行过程中,ZAB协议会一直运行于阶段三进行消息广播,出现Leader崩溃和其他原因导致的Leader丢失,ZAB协议就会再次进入阶段一,重新选举新的Leader。
ZAB协议的设计中,每个进程都有可能处于以下三个状态之一:
- LOOKING:Leader选举阶段
- FOLLOWING:Follower服务器的状态,表示此时和Leader保持同步
- LEADING:Leader服务器的状态,表示当前自己是作为主线程领导。
集群启动时所有进程程都处于LOOKING状态,选举出Leader后各个进程根据自己角色进入相应状态,Leader崩溃或放弃领导地位时,所有进程重新进入LOOKING状态。
Leader为每一个与自己保持同步的Follower都创建一个 *** 作队列,同一时间,一个Follower只能和一个Leader保持同步,Leader和Follower通过心跳检测来感应彼此的状态。如果Leader不能在超市时间内收到过半Follower的心跳检测,或者是TCP连接本身断开,那么Leader就会终止当前的领导,所有Follower放弃这个Leader,转换到LOOKING状态
重新选举,选举完毕进入新一轮的主进程周期。
- 都有一个类似于Leader的角色,负责协调Follower的运行
- Leader收到过半数的Follower作为正确反馈后,才对提案进行提交
- ZAB协议中,每个Proposal都包含了一个epoch值用来代表当前Leader周期,Poxos算法也同样存在这样一个标识Ballot。
Paxos算法的包含两个阶段,第一阶段为读阶段,Leader会通过与其他进程通信来收集上一个主进程提出的提案,并将他们提交。第二阶段称为写阶段,这个阶段主线程开始提出自己的提案。
ZAB协议在和Paxos算法的读阶段类似的发现阶段之后新增了一个同步阶段,用来确保存在过半的Leader已经提交了之前Leader周期的所有事务,同步阶段有效保证主线程在提出事务之前,所有的进程已经完成了对之前所有事务的提交,完成同步阶段,ZAB协议就进入和Poxos算法写阶段类似的广播阶段。
Paxos和ZAB算法的本质区别在于,设计目标不同,ZAB协议主要构建一个高可用的分布式数据主备系统,而Paxos算法则用于构建一个分布式的一致性状态机系统。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)