Zab(Zookeeper Atomic Broadcast)协议
更多内容,前往 IT-BLOG
一、什么是 Zab协议
Zab( Zookeeper Atomic Broadcast:Zookeeper原子广播)Zookeeper 通过 Zab 协议保证分布式事务的最终一致性。
【1】Zab协议是为分布式协调服务 Zookeeper专门设计的,是 Zookeeper保证数据一致性的核心算法。Zab借鉴了 Paxos算法,但又不像 Paxos那样,是一种通用的分布式一致性算法。支持崩溃恢复 和 原子广播协议。
【2】在 Zookeeper中主要依赖 Zab协议来实现数据一致性,基于该协议,zk实现了一种主备模型(即 Leader和 Follower模型)的系统架构来保证集群中各副本之间数据的一致性。主备系统架构模型指只有一台客户端(Leader)负责处理外部的写事务请求,然后 Leader客户端将数据同步到其他 Follower节点。
Zookeeper 客户端会随机的连接到 Zookeeper
集群中的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会向 Leader 提交事务,Leader
接收到事务提交,会广播该事务,只要超过半数节点写入成功,该事务就会被提交。
就这样,客户端发送来的写请求,全部给 Leader,然后 Leader再转给 Follower。这时候需要解决两个问题:
【1】Leader服务器是如何把数据更新到所有的 Follower的;
【2】Leader服务器突然间失效了,怎么办?
因此 ZAB协议为了解决上面两个问题,设计了两种模式:
【1】消息广播模式:把数据更新到所有的 Follower;
【2】崩溃恢复模式:Leader发生崩溃时,如何恢复;
二、消息广播模式
在 Zookeeper集群中数据副本的传递策略就是采用消息广播模式。如果你了解 2PC协议的话,理解起来就简单很多了,消息广播的过程实际上是一个简化版本的二阶段提交过程。与二阶段提交相似但是却又不同。二阶段提交的要求协调者必须等到所有的参与者全部反馈
ACK确认消息后,再发送 commit消息。要求所有的参与者要么全部成功要么全部失败。二阶段提交会产生严重阻塞问题。我们来看一下
ZAB的这个过程:ZAB协议中 Leader等待 Follower的 ACK反馈是指 “只要半数以上的Follower成功反馈即可,不需要收到全部 Follower反馈”
【1】Leader将客户端的 Request转化成一个 Proposal(提议)。同时为每个 proposal分配一个全局唯一ID,即ZXID;
【2】Leader节点再数据写完之后,将向所有的 Follower节点发送数据广播请求(或数据复制),等待所有的 Follower节点反馈。Leader为每一个 Follower准备了一个 FIFO队列,并把 Proposal(带有 zxid的消息)发送到队列上;
【3】Follower从队列中取出消息处理完(写入本地事物日志中或将 proposal写到磁盘)后,向 Leader服务器发送 ACK确认。Leader若收到 Follower的半数以上 ACK反馈。Leader就会向所有的 Follower发送 commit。即将 Leader节点上的数据同步到 Follower节点之上;
Leader服务器与每个 Follower之间都有一个单独的队列进行收发消息,使用队列消息可以做到异步解耦。Leader和 Follower之间只要往队列中发送了消息即可。如果使用同步方式容易引起阻塞。性能上要下降很多。
三、崩溃恢复模式
当整个集群正在启动时,或者当 Leader节点出现网络中断、崩溃等情况时,ZAB协议就会进入恢复模式并选举产生新的 Leader,当 Leader服务器选举出来后,并且集群中有过半的机器和该 Leader节点完成数据同步后(同步指的是数据同步,用来保证集群中过半的机器能够和 Leader服务器的数据状态保持一致),ZAB协议就会退出恢复模式。
当集群中已经有过半的 Follower节点完成了和
Leader状态同步以后,那么整个集群就进入了消息广播模式。这个时候,在 Leader节点正常工作时,启动一台新的服务器加入到集群,那这个服务器会直接进入数据恢复模式,和
Leader节点进行数据同步。同步完成后即可正常对外提供非事务请求的处理。
【1】Zookeeper集群中为保证所有进程能够有序的顺序执行,只能是 Leader服务器接受写请求,即使是 Follower服务器接受到客户端的请求,也会转发到 Leader服务器进行处理。
【2】如果 Leader服务器发生崩溃,则 zab协议要求 Zookeeper集群进行崩溃恢复和 Leader服务器选举。
ZAB协议崩溃恢复要求满足如下2个要求:
【1】已经被处理的消息不能丢失:当
Leader收到合法数量 Follower的 ack后,就向各个 Follower广播 commit命令,同时也会在本地执行
commit并向连接的客户端返回「成功」。但是如果各个 Follower在收到 commit命令前
Leader就挂了,导致剩下的服务器并没有执行到这条消息。
Leader对事务消息发起 commit操作,该消息在 Follower1上执行了,但是
Follower2还没有收到commit,Leader就已经挂了,而实际上客户端已经收到该事务消息处理成功的回执了。所以在
zab协议下需要保证所有机器都要执行这个事务消息,必须满足已经被处理的消息不能丢失。
【2】被丢弃的消息不能再次出现:当
Leader接收到消息请求生成 proposal后就挂了,其他 Follower并没有收到此proposal,因此经过恢复模式重新选了
Leader后,这条消息应跳过。 此时,之前挂了的 Leader重新启动并注册成了
Follower,他保留了被跳过消息的proposal状态,与整个系统的状态是不一致的,需要将其删除。(Leader都换代了,所以以前
Leader的 proposal失效了)。
针对崩溃恢复的两种情况分析:ZAB协议需要满足上面两种情况,就必须要设计一个 Leader选举算法,能够确保已经被 Leader提交的事务 Proposal能够提交、同时丢弃已经被跳过的事务Proposal。如果 Leader选举算法能够保证新选举出来的 Leader服务器拥有集群中所有机器最大 ZXID编号的事务Proposal,那么就可以保证这个新选举出来的 Leader一定具有已经提交的提案。因为所有提案被 commit之前必须有超过半数的 Follower ack,即必须有超过半数节点的服务器的事务日志上有该提案的 Proposal,因此只要有合法数量的节点正常工作,就必然有一个节点保存了所有被 commit消息的 Proposal状态。
另外一个,zxid是64位,高32位是epoch编号,每经过一次 Leader选举产生一个新的 Leader,新的 Leader会将 epoch号+1,低32位是消息计数器,每接收到一条消息这个值+1,新 Leader选举后这个值重置为0。这样设计的好处在于老的 Leader挂了以后重启,它不会被选举为 Leader,因此此时它的 zxid肯定小于当前新的 Leader。当老的 Leader作为 Follower接入新的 Leader后,新的 Leader会让它将所有的拥有旧的 epoch号的未被 commit的 Proposal清除。
Leader服务器发生崩溃时分为如下场景:
【1】Leader在提出 Proposal时未提交之前崩溃,则经过崩溃恢复之后,新选举的 Leader一定不能是刚才的 Leader。因为这个 Leader存在未提交的 Proposal;
【2】Leader在发送 commit消息之后,崩溃。即消息已经发送到队列中。经过崩溃恢复之后,参与选举的 Follower服务器(刚才崩溃的
Leader有可能已经恢复运行,也属于 Follower节点范畴)中有的节点已经是消费了队列中所有的 commit消息。即该
Follower节点将会被选举为最新的 Leader。剩下动作就是数据同步过程。
四、数据同步
在 Zookeeper集群中新的 Leader选举成功之后,Leader会将自身的提交的最大 Proposal的事务 ZXID发送给其他的 Follower节点。Follower节点会根据 Leader的消息进行回退或者是数据同步操作。最终目的要保证集群中所有节点的数据副本保持一致。
数据同步完之后,Zookeeper集群如何保证新选举的 Leader分配的
ZXID是全局唯一呢?这个就要从ZXID的设计谈起。ZXID是一个长度64位的数字,其中低32位是按照数字递增,即每次客户端发起一个
Proposal,低32位的数字简单加1。高32位是 Leader周期的
epoch编号,每当选举出一个新的 Leader时,新的 Leader就从本地事物日志中取出
ZXID,然后解析出高32位的 epoch编号,进行加1,再将低32位的全部设置为0。这样就保证了每次新选举的
Leader后,保证了 ZXID的唯一性而且是保证递增的。
五、ZAB协议原理
ZAB协议要求每个 Leader都要经历三个阶段,即发现,同步,广播。
发现:要求 zookeeper集群必须选择出一个 Leader,同时 Leader会维护一个 Follower可用列表。客户端可以跟 Follower中的节点进行通信。
同步:Leader要负责将本身的数据与 Follower完成同步,做到多副本存储。这样也是体现了 CAP中高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事物日志中。
广播:Leader可以接受客户端新的 proposal请求,将新的 proposal请求广播给所有的 Follower。