ZAB协议

概述:

在深入了解ZooKeeper之前,相信很多读者都会认为ZooKeeper就是Paxos算法的一个实现。但事实上,ZooKeeper并没有完全果用Paxos算法,而是使用了一种称为ZooKeeperAtomic Broadcast ( ZAB, ZooKeeper原子消息广播协议)的协议作为其数据一致性的核心算法。
ZAB协议的核心是定义了对于那些会改变ZooKeeper服务器数据状态的事务请求的处理方式,即:
所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而余下的其他服务器则成为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower 服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息.妥求其将前一个Proposal进行提交。
注意:
ZAB协议和Paxos算法的本质区别在于,两者的设计目标不太一样。ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如ZooKeeper,而Paxos算法则是用于构建一个分布式的一致性状态机系统。

ZAB协议包括两种基本的模式:

ZAB协议包括两种基本的模式:

当Leader服务器出现网络中断、崩愤退出,重启与集群中已经不存在过半的服务器与该Leader服务器保持正常通信等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,一个机器要成为新的Leader,必须获得过半进程的支持。ZAB协议就会退出恢复模式。所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。
当一台遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到Leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。ZooKeeper设计成只允许唯一的一个Leader服务器来进行事务请求的处理。Leader服务器在接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议: 而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。
举个例子来说:
一个由3台机器组成的ZAB服务,通常由1个Leader、2个Follower服务器组成。某一个时刻,假如其中一个Follower服务器挂了,整个ZAB集群是不会中断服务的,这是因为Leader服务器依然能够获得过半机器(包括Leader自己)的支持。

消息广播:

ZAB协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。针对客户端的事务请求,Leader服务器会为其生成对应的事务Proposal,并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交,如下图所示就是ZAB协议消息广播流程的示意图。
这里写图片描述
此处ZAB协议中涉及的真正的二阶段提交过程则与其略有不同。在ZAB协议的二阶段提交过程中,移除了中断逻辑,所有的Follower服务器要么正常反馈Leader提出的事务Proposal ,要么就抛弃Leader服务器。同时,ZAB协议将二阶段提交中的中断逻辑移除意味着我们可以在过半的Follower服务器已经反馈Ack之后就开始提交事务Proposal了,而不需要等待集群中所有的Follower服务器都反馈响应。当然,在这种简化了的二阶段提交模型下,是无法处理Leader服务器崩溃退出而带来的数据不一致问题的,因此在ZAB协议中添加了崩愤恢复模式来解决这个问题。另外,整个消息广播协议是基于具有FIFO特性的TCP协议来进行网络通信的,因此能够很容易地保证消息广播过程中消息接收与发送的顺序性。
在整个消息广播过程中,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消息后,也会完成对事务的提交。

崩愤恢复:

一旦Leader服务器出现崩愤,或者说由于网络原因导致Leader服务器失去了与过半Follower的联系,那么就会进入崩愤恢复模式。在ZAB协议中,为了保证程序的正确运行,整个恢复过程结束后需要选举出一个新的Leader服务器。ZAB协议规定了如果一个事务Proposal在一台机器上被处理成功,那么应该在所有的机器上都被处理成功,哪怕机器出现故障奔溃。

奔溃恢复时ZAB需要满足的两点基本特性:
1)ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交
假设一个事务在Leader服务器上被提交了,并且已经得到过半Follower服务器的Ack反馈,但是在它将Commit消息发送给所有Follower机器之前,Leader服务器挂了
2)ZAB协议需要确保丢弃那些只在leader服务器上被提出还未提交的事务
假设初始的Leader服务器Server1在提出了一个事务Proposal3之后就奔溃退出了,从而导致集群中的其他服务器都没有收到这个事务Proposal 。于是当Server1恢复过来再次加入到集群中的时候,ZAB协议需要确保丢弃Proposal3这个事务。
结合上面提到的这两个崩愤恢复过程中需要处理的特殊情况,就决定了ZAB协议必须设计这样一个Leader选举算怯:能够确保提交已经被Leader提交的事务Proposal,同时丢弃已经被跳过的事务Proposal。针对这个要求,如果让Leader选举算怯能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号( 即ZXID 最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。更为重要的是,如果让具有最高编号事务Proposal的机器来成为Leader,就可以省去Leader服务器检查Proposal的提交和丢弃工作的这一步操作了。

数据同步:

在Leader服务器上提交的事务最终被所有服务器都提交完成Leader选举之后, 在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,Leader服务器会首先确认事务日志中的所有Proposal是否都已经被集群中过半的机器提交了,即是否完成数据同步。ZAB协议的数据同步过程如下:
1)正常情况下的数据同步:
所有正常运行的服务器,要么成为Leader,要么成为Follower并和Leader保持同步。Leader服务器需要确保所有的Follower服务器能够接收到每一条事务Proposal,并且能
够正确地将所有已经提交了的事务Proposal应用到内存数据库中去。具体的,Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Followe 服务器,并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交。等到Follower服务器将所有其尚未同步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数据库中后,Leader服务器就会将该Follower服务器加入到真正的可用Follower列表中,并开始之后的其他流程。
2)异常情况:处理那些需要被丢弃的事务Proposal(leader服务器上被提出还未提交的事务)
在ZAB协议的事务编号ZXID设计中,ZXID是一个64位的数字,其中低32位可以看作是一个简单的递增的计数器,针对客户端的每一个事务请求,Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作;而高32位则代表了Leader周期epoch的编号,每当选举产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务Proposal的ZXID ,并从该ZXID中解析出对应的epocb值,然后再对其进行加1操作,之后就会以此编号作为新的epocb,并将低32位置0来开始生成新的ZXID。ZAB协议中的这一通过epoch编号来区分Leader周期变化的策略,能够有效地避免不同的Leader服务器错误地使用相同的ZXID编号提出不一样的事务Proposal的异常情况,这对于识别在Leader崩愤恢复前后生成的Proposal非常有帮助,大大简化和提升了数据恢复流程。基于这样的策略,当一个包含了上一个Leader周期中尚未提交过的事务Proposal的服务器启动时(即之前死掉的Leader),其肯定无越成为Leader,原因很简单,因为当前集群中一定包含一个Quorum集合,该集合中的机器一定包含了更高epoch的事务Proposal ,因此这台机器的事务Proposal肯定不是最高,也就无怯成为Leader了。当这台机器加入到集群中,以Follower角色连接上Leader服务器之后,Leader服务器会根据自己服务器上最后被提交的Proposal来和Follower服务器的Proposal进行比对,比对的结果当然是Leader会要求Follower进行一个回退操作一一回退到一个确实已经被集群中过半机器提交的最新的事务Proposal。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值