ZAB协议全称:Zookeeper Atomic Broadcast(Zookeeper原子广播协议)。
整个ZooKeeper就是一个多节点分布式一致性算法的实现,底层采用的实现协议是ZAB。
ZooKeeper是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,ZooKeeper并没有使用Paxos,而是采用了ZAB协议,ZAB是Paxos算法的一种简化实现。
ZAB协议定义:ZAB协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复和原子广播的协议。下面我们会重点讲这两个东西。
基于该协议,ZooKeeper实现了一种主备模式的系统架构来保持集群中各个副本之间数据一致性。具体如下图所示:
上图显示了ZooKeeper如何处理集群中的数据。所有客户端写入数据都是写入到Leader节点,然后由Leader复制到Follower节点中,从而保证数据一致性。
那么复制过程又是如何的呢?
复制过程类似两阶段提交(2PC),ZAB只需要Follower(含leader自己的ack)有一半以上返回Ack信息就可以执行提交,大大减小了同步阻塞,也提高了可用性。
简单介绍完,开始重点介绍消息广播和崩溃恢复。整个ZooKeeper就是在这两个模式之间切换。 简而言之,当 Leader服务可以正常使用,就进入消息广播模式,当Leader不可用时,则进入崩溃恢复模式。
ZAB协议的消息广播过程使用的是一个原子广播协议,类似一个两阶段提交过程。对于客户端发送的写请求,全部由Leader接收,Leader将请求封装成一个事务Proposal,将其发送给所有Follwer,然后根据所有Follwer的反馈,如果超过半数(含leader自己)成功响应,则执行commit操作。
整个广播流程如下:
通过以上步骤,就能够保持集群之间数据的一致性。
大概步骤:
还有一些细节:
刚刚我们说消息广播过程中,Leader崩溃怎么办?还能保证数据一致吗?
实际上,当Leader崩溃,即进入我们开头所说的崩溃恢复模式(崩溃即:Leader失去与过半Follwer的联系)。下面来详细讲述。
两个假设:
针对这些问题,ZAB定义了 2 个原则:
所以,ZAB设计了下面这样一个选举算法:
能够确保提交已经被Leader提交的事务,同时丢弃已经被跳过的事务。
针对这个要求,如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器ZXID最大的事务,那么就能够保证这个新选举出来的Leader一定具有所有已经提交的提案。
而且这么做有一个好处是:可以省去Leader服务器检查事务的提交和丢弃工作的这一步操作。
当崩溃恢复之后,需要在正式工作之前(接收客户端请求),Leader服务器首先确认事务是否都已经被过半的Follwer提交了,即是否完成了数据同步。目的是为了保持数据一致。
当Follwer服务器成功同步之后,Leader会将这些服务器加入到可用服务器列表中。
实际上,Leader服务器处理或丢弃事务都是依赖着ZXID的,那么这个ZXID如何生成呢?
在ZAB协议的事务编号ZXID设计中,ZXID是一个64位的数字,其中低32位可以看作是一个简单的递增的计数器(Aotmic原子类实现),针对客户端的每一个事务请求,Leader都会产生一个新的事务Proposal并对该计数器进行+1操作。
而高32位则代表了Leader服务器上取出本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的 epoch值(leader选举周),当一轮新的选举结束后,会对这个值加一,并且事务id又从0开始自增。
高32位代表了每代Leader的唯一性,低32代表了每代Leader中事务的唯一性。同时,也能让Follwer通过高32位识别不同的Leader。简化了数据恢复流程。
基于这样的策略:当Follower连接上Leader之后,Leader服务器会根据自己服务器上最后被提交的ZXID和Follower上的ZXID进行比对,比对结果要么回滚,要么和Leader同步。
ZK开始不支持Netty,通过NIO与客户端通讯,官方建议使用Netty;
BIO用于集群节点间的通讯,因为BIO比NIO简单不容易出错,而且集群中的BIO链接不会很多,使用BIO效果更优;
什么是脑裂?
当ZK集群发生网络分区时,原来的Leader被隔离了,新的Leader在新的集群中通过选举产生,此时对外提供服务就有两个Leader节点了,此时就存在脑裂问题。
如何解决?
当集群存在多个Leader时,读数据时可能存在数据不一致情况(其实,正常的集群中由于ZK是半数即成功也会存在读数据不一致的情况,保证最终一致性),但是在老的Leader上只能读到已提交的内存数据,未提交的数据只会存在于磁盘log文件中。Leader会定时地发ping命令与所有Follower保持联系,当联系流失时Leader会变成LOOKING状态重新发起选举。当分区恢复后,节点之间建立连接时会比对epoch纪元,新的Leader纪元比老Leader大,所以老的Leader会同步新Leader数据并更新自己为Follower。