ZAB协议概要
一) ZAB协议是专门为ZooKeeper实现分布式协调功能而设计。ZK主要是依据ZAB协议来实现分布式系统的数据一致性。
二) ZooKeeper根据ZAB协议建立了主备模型来完成ZK集群中数据的同步。这里所说的主备系统架构模型是指在zookeeper集群中只有一个Leader负责处理外部客户端的事务请求(或者说是写操作),之后再由该Leader服务器将客户端的写操作数据同步到所有的Follower节点中。其他节点主要提供读操作,也可以接收写操作,接收写操作后会转到Leader节点。
三) ZAB的协议核心是在整个ZooKeeper集群中只有一个节点即Leader将客户端的写操作转化为事务(或提议proposal)。Leader节点在数据写完成之后,将会向所有的Follower节点发送数据广播请求(或者说数据复制请求),等待所有Follower节点的反馈。在ZAB协议中,只要超过半数Follower节点反馈OK,Leader节点就会向所有的Follower服务器发送commit消息,即将Leader节点上的数据同步到Follower节点之上。
四) ZAB协议中主要有两种运行模式:第一是消息广播模式;第二是崩溃恢复模式;
1)消息广播模式
A. 在ZooKeeper集群中数据副本的传递策略就是采用消息广播模式。ZK中数据副本的同步方式与两阶段提交相似但是却又不同。两阶段提交的要求协调者必须等到所有的参与者全部反馈ACK确认消息后,再发送commit消息,即要求所有的参与者要么全部成功要么全部失败,两阶段提交会产生严重的阻塞问题。ZAB协议只需要协调者Follower半数以上返回OK确认即可发送commit消息。
B. ZAB协议中Leader等待Follower的ACK反馈是指“只要半数以上的Follower成功反馈即可,不需要收到全部Follower的反馈”。
C. 图中展示了消息广播的具体流程图:
D. Zookeeper中消息广播的具体步骤如下:
- 客户端发起一个写操作请求
- Leader服务器将客户端的request请求转化为事物proposal提议,同时为每个proposal分配一个全局唯一的ID,即ZXID。
- leader服务器与每个follower之间都有一个队列,leader将消息发送到该队列
. follower机器从队列中取出消息处理完(写入本地事物日志中)毕后,向leader服务器发送ACK确认。 - leader服务器收到半数以上的follower的ACK后,即认为可以发送commit
- leader向所有的follower服务器发送commit消息。
E. Zookeeper采用ZAB协议的核心就是只要有一台服务器提交了proposal,就要确保所有的服务器最终都能正确提交proposal。这也是CAP/BASE最终实现一致性的一个体现。
F. Leader服务器与每个follower之间都有一个单独的队列进行收发消息,使用队列消息可以做到异步解耦。leader和follower之间只要往队列中发送了消息即可。如果使用同步方式容易引起阻塞。性能上要下降很多。
2)崩溃恢复模式
A. Zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是Leader服务器接受写请求,即使是Follower服务器接受到客户端的写请求,也会将写请求转发到Leader服务器进行处理。
B. 如果Leader服务器发生崩溃,则ZAB协议要求Zookeeper集群进行崩溃恢复和Leader服务器选举。
C. ZAB协议崩溃恢复要求满足如下2个要求:
- 确保已经被Leader提交的proposal必须最终提交到所有的Follower服务器。
- 确保丢弃已经被Leader发出但是没有被提交的proposal提议。
D. 根据上述要求,新选举出来的Leader不能包含未提交的proposal提议,即新选举的Leader必须都是已经提交了的proposal的Follower节点。同时新选举的Leader节点中含有最高的ZXID。这样做的好处就为了避免了Leader节点检查proposal提议的提交和丢弃工作。
E. Leader服务器发生崩溃时分为如下场景:
- Leader在提出proposal时未提交之前崩溃,则经过崩溃恢复之后,新选举的Leader一定不能是刚才的Leader。因为这个Leader存在未提交的proposal提议。
- 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。
Zookeeper设计目标
Zookeeper作为当今最流行的分布式系统应用协调框架,采用ZAB协议的最大目标就是建立一个高可用、可扩展的分布式数据主备系统。即在任何时刻只要Leader发生宕机,都能保证分布式系统中数据的可靠性和最终一致性。
深刻理解ZAB协议,才能更好的理解Zookeeper对于分布式系统建设的重要性。以及为什么采用Zookeeper就能保证分布式系统中数据的最终一致性以及服务的高可用性。