也可以在我的个人网站中查看该文章?02-Zookeeper理论相关——Paxos和ZAB
文章目录
02-Zookeeper理论相关——Paxos和ZAB
攘其外必先安其内
Paxos和ZAB
Paxos算法
关于Paxos算法相关介绍和讲解的可以看上面链接的文章,通俗易懂,去看完再看该篇剩下的。
ZAB协议
- 事实上,zk并没有完全采用Paxos 算法,而是使用了Zookeeper Atomic Broadcast即zookeeper原子广播协议作为其数据一致的核心算法。
- ZAB是为zk专门设计的支持崩崩溃恢复的原子广播。
- zk主要依赖ZAB协议来实现分布式数据的一致性。基于该协议,zk实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性。
- 具体的,zk使用一个单一的主进程来接收并处理client的所有事务请求,并采用ZAB原子广播协议,将server数据的状态变更为事务 Proposa(提案)的形式广播到所有的副本进程上。
- 顺序性:由于顺序执行的状态变更前后会存在一定的依赖关系,ZAB协议需要保证保证若一个状态变更被处理时,所有其依赖的状态变更都应该已经被处理了。
- 高可用性:考虑到主进程可能崩溃,ZAB协议还需要在主进程崩溃时保持正常工作。
协议介绍
- ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。
- 当整个服务框架在启动过程中,或是当leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的leader服务器。当选举产生了新的leader服务器,同时集群中已有过半的机器(follwoer)与leader完成状态同步(即数据同步,用以保证集群中存在过半的机器与leader的数据状态保持一致)后,退出恢复模式。
- 过半机器同步完成后,整个服务框架就可以进入消息广播模式了。启动后新加入的机器会自觉进入数据恢复模式(即找到leader并进行数据同步)。
- leader server是唯一允许处理事务请求的机器,若其他机器接收到client的事务请求,会将该请求转发给leader;接着,leader会生成相应的事务提案并发起一轮广播协议。
消息广播
- ZAB协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。
- 当过半的follower反馈ack之后就可以提交事务Proposal(提案)了。
- 但是该模型无法处理leader崩溃退出而带来的数据不一致问题。因此,在ZAB协议中添加了另一模式,即采用崩溃恢复模式来解决这个问题。
- 另外,整个消息广播协议是基于具有FIFO特性的TCP协议来进行网络通信的,因此能够很容易地保证消息广播过程中消息接收与发送的顺序性。(zk采用单一主线程来处理client请求)
- 在整个消息广播中,leader服务器会为每个事物请求生成对应的Proposal(提案)来进行广播,并且在广播事物Proposal之前,为Proposal分配一个全局单调递增的唯一ID,即事物ID(ZXID)。
- 具体而言,在消息广播过程中,leader server会为每个follower分配一个单独的队列,然后将需要广播的事务proposal依次放入这些队列中,并且根据FIFO策略发送消息。每个follower接收到proposal后,会首先将其以事务日志形式写入本地磁盘,并且在成功写入后(ack)反馈给leader。当leader收到超过半数的ack后,会广播一个commit消息给所有的follower以通知其进行事务提交,同时leader自身也会完成对事务的提交,尔follower也会在接收到commit消息后,完成对事务的提交。(提交至内存)
整个消息广播流程本质上是一个2PC(两阶段提交)过程:
- leader发送事务proposal --> follower将proposal以事务形式写入本地磁盘,并且反馈ack给leader。
- leader在收到半数ack后,会广播一个commit消息给所有follower --> leader完成事务提交 --> follower完成事务的提交。
崩溃恢复
当leader崩溃或者leader是去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举一个新的leader,让所有的server都恢复到一个正确的状态。
zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos实现的。系统默认的选举算法是fast paxos。
zookeeper选主流程(basic paxos)
- 选举线程由当前server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的server;
- 选举线程首先向所有server发起一次询问(包括自己);
- 选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
- 收到所哟偶server回复后,就计算出zxid最大的那个server,并将这个server相关信息设置成下一次要投票的server;
- 线程将当前zxid最大的server设置为当前server要推荐的leader,如果此时获胜的server获得(n/2 + 1)的server票数,设置当前推荐的leader为获胜的server,将根据获胜的server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。
- 要使leader获得多数server的支持,则server总数必须是奇数 2n+1,且存活的server的数目不得少于 n+1,每个server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。
zookeeper选主流程(fast paxos)
fast paxos流程是在选举过程中,某server首先向所有server提议自己要成为leader,当其他server收到提议后,解决epoch和zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出leader。
数据同步
- 完成leader选举后,在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,leader server会首先确认事务日志中的所有proposal是否都已经被集群中过半的机器提交了,即是否完成数据同步。
- ZAB协议的数据同步过程:
- 首先,leader需要确保所有follower能够接收到每一条proposal,并且能正确地将所有已提交的proposal应用到内存数据库中。
- leader为每个follower准备一个队列,并将未被各follower同步的事务以proposal消息的形式逐个发送给follower,并在每个proposal后紧接着一个commit消息,表明该事务已提交。
- 等到follower将其所有尚未同步的事务proposal都从leader上同步过来并成功应用到本地数据库中后,leader会将follower加入到真正的可用follower列表中。
- 首先,leader需要确保所有follower能够接收到每一条proposal,并且能正确地将所有已提交的proposal应用到内存数据库中。
- 还需要考虑如何处理那些需要被丢弃的事务proposal:
- ZAB的事务编号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的server启动时肯定无法成为leader,因为其不可能拥有最高ZXID的proposal。那么该server会成为follower,当其连接上leader后,会被leader要求进行回退操作,即回退到一个确实已经被集群中过半机器提交的最新的proposal。
小结
zookeeper工作原理
zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做ZAB协议。ZAB协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,ZAB就进入了恢复模式,当领导者被选举出来,且大多数server完成了和leader的状态同步后,恢复模式就结束了。状态同步保证了leader和server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出时加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于哪个leader的统治时期。低32位用于递增计数。