概述
ZAB全称Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。其借鉴了 Paxos 算法,是特别为 Zookeeper 设计的支持崩溃恢复的原子广播协议。基于该协议,Zookeeper 设计为只有一台客户端(Leader)负责处理外部的写事务请求,然后Leader 客户端将数据同步到其他 Follower 节点。即 Zookeeper 只有一个 Leader 可以发起提案,解决了Paxos算法中可能出现活锁的情况。
ZAB协议详解
ZAB协助主要作用于Zookeeper的两种模式:消息广播和崩溃恢复。
消息广播
消息广播指的是Zookpeeper客户端发起写请求,Zookeeper的Leader需要把消息广播到集群中的每个结点中的过程。在这个过程中,需要保证消息的顺序一致性。
顺序一致性: Zookeper的请求必须保持全局有序,举个例子来理解,Zookeeper是一个树形结构,很多操作都要先检查才能确定是否可以执行,比如P1的事务t1可能是创建节点"/a",t2可能是创建节点"/a/bb",只有先创建了父节点"/a",才能创建子节点"/a/b"。
整个消息广播类似于一个两阶段提交的过程:
- 广播事务阶段
- 广播提交阶段
具体流程如下:
- 客户端发起写请求
- Leader服务器将客户端的请求转化为事务Proposal提案,同时为每个Proposal分配一个全局递增的ID, 即zxid。
- Leader服务器为每个Follower服务器分配一个单独的队列,然后将需要广播的 Proposal依次放到队列中去,并且根据FIFO策略进行消息发送,保证消息的顺序一致性。
- Follower接收到Proposal后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向Leader反馈一个Ack响应消息。
- Leader接收到超过半数以上Follower的Ack响应消息后,包括Leader节点自己,即认为消息发送成功,可以发送commit消息。
- Leader向所有Follower广播commit消息,同时自身也会完成事务提交。Follower 接收到commit消息后,会将上一条事务提交。
Zookeeper采用Zab协议的核心,就是只要有一台服务器提交了Proposal,就要确保所有的服务器最终都能正确提交Proposal。
崩溃恢复
一旦Leader服务器出现崩溃或者由于网络原因导致Leader服务器失去了与过半 Follower的联系,那么就会进入崩溃恢复模式。
崩溃恢复主要包括两部分:Leader选举和数据恢复。
Leader选举阶段
Zab协议需要保证选举出来的Leader需要满足以下条件:
- 新选举出来的Leader不能包含未提交的Proposal。即新Leader必须都是已经提交了Proposal的Follower服务器节点。
- 新选举的Leader节点中含有最大的zxid。这样做的好处是可以避免Leader服务器检查Proposal的提交和丢弃工作。
数据恢复阶段
- 完成Leader选举后,在正式开始工作之前(接收事务请求,然后提出新的Proposal),Leader服务器会首先确认事务日志中的所有的Proposal 是否已经被集群中过半的服务器Commit。
- Leader服务器需要确保所有的Follower服务器能够接收到每一条事务的Proposal,并且能将所有已经提交的事务Proposal应用到内存数据中。等到Follower将所有尚未同步的事务Proposal都从Leader服务器上同步过,并且应用到内存数据中以后,Leader才会把该Follower加入到真正可用的Follower列表中。
由于Leader崩溃,会导致之前的数据提交出现异常,见下图:
- 假如一个事务在Leader发起提案后,发送给Follower之后,挂了,该如何处理?
答: 会丢弃处理
- 加入一个事务在Leader发起提案后,得到过半Follower的应答,同时部分commit给Follower后崩溃,该怎么呢?
答: 会提交事务
针对这些问题,ZAB 定义了 2 个原则:
- ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
- ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
一句话总结:能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务。
这也是为什么Leader选举的时候选举zxid最大的Follower。
总结
我们学习了上面的内容以后,同时有两个问题抛给大家。
- Zookeeper 到底是不是强一致性?
不保证强一致性,但是保证顺序一致性。因为 Leader 再发送 commit 消息给所有 Follower 和 Observer 后,它们并不是同时完成 commit 的。比如因为网络原因,不同节点收到的 commit 较晚,那么提交的时间也较晚,就会出现多个节点的数据不一致,但是经过短暂的时间后,所有节点都 commit 后,数据就保持同步了。
另外 Zookeeper 支持强一致性,就是手动调用 sync 方法来保证所有节点都 commit 才算成功。
- ZAB 的顺序一致性怎么做到的?
- 只有Leader节点接受事务请求,进行消息广播
- Leader 发送 proposal 时,其实会为每个 Follower 创建一个队列,都往各自的队列中发送 proposal。Leader 收到请求后,依次放到队列中,然后 Follower 依次从队列中获取请求,这样就保证了数据的顺序性。