首先介绍下什么是Zookeeper的ZAB协议,什么是ZAB(Zookeeper Atomic Broadcast)协议
什么是ZAB协议
ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议。ZAB 协议包括两种基本的模式:崩溃恢复和消息广播。Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议,ZAB是Paxos算法的一种简化实现。
当整个 Zookeeper 集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有服务器进入崩溃恢复模式,首先选举产生新的 Leader 服务器,然后集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步。当集群中超过半数机器与该 Leader 服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader 服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。
基于该协议,Zookeeper 实现了一种 主备模式 的系统架构来保持集群中各个副本之间数据一致性。具体如下图所示:
上图显示了 Zookeeper 如何处理集群中的数据。所有客户端写入数据都是写入到Leader节点,然后,由 Leader 复制到Follower节点中,从而保证数据一致性。
那么复制过程又是如何的呢?复制过程类似两阶段提交(2PC),ZAB 只需要 Follower(含leader自己的ack) 有一半以上返回 Ack 信息就可以执行提交,大大减小了同步阻塞。也提高了可用性。
两阶段提交协议 2PC
1. 第一阶段(投票阶段):
(1)协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应;
(2)参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。 (3)各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
2. 第二阶段(提交执行阶段):
当协调者节点从所有参与者节点获得的相应消息都为”同意”时:
(1)协调者节点向所有参与者节点发出”正式提交(commit)”的请求;
(2)参与者节点正式完成操作,并释放在整个事务期间内占用的资源;
(3)参与者节点向协调者节点发送”完成”消息;
(4)协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。
两阶段提交存在的问题:
1. 执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态;
2. 参与者发生故障:协调者需要给每个参与者额外指定超时机制,超时后整个事务失败;
3. 协调者发生故障:参与者会一直阻塞下去。需要额外的备机进行容错;
4. 二阶段无法解决的问题:协调者再发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
三阶段提交协议 3PC
与两阶段提交不同的是,三阶段提交有两个改动点:
1. 引入超时机制。同时在协调者和参与者中都引入超时机制;
2. 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
也就是说,除了引入超时机制之外,3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。
1. CanCommit 阶段 3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。
(1)事务询问:协调者向参与者发送 CanCommit 请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。
(2)响应反馈:参与者接到 CanCommit 请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回 Yes 响应,并进入预备状态。否则反馈 No。
2. PreCommit 阶段 协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。根据响应情况,有以下两种可能:
假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会执行事务的预执行。
(1)发送预提交请求:协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段。
(2)事务预提交:参与者接收到 PreCommit 请求后,会执行事务操作,并将 undo 和 redo 信息记录到事务日志中。 (3)响应反馈:如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。
假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。 (1)发送中断请求:协调者向所有参与者发送 abort 请求。 (2)中断事务:参与者收到来自协调者的 abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
3. doCommit 阶段 该阶段进行真正的事务提交,也可以分为以下两种情况。
3.1 执行提交
(1)发送提交请求:协调接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
(2)事务提交:参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
(3)响应反馈:事务提交完之后,向协调者发送 ACK 响应。
(4)完成事务:协调者接收到所有参与者的 ACK 响应之后,完成事务。
3.2 中断事务
协调者没有接收到参与者发送的 ACK 响应(可能是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务。
(1)发送中断请求:协调者向所有参与者发送 abort 请求。
(2)事务回滚:参与者接收到 abort 请求之后,利用其在阶段二记录的 undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
(3)反馈结果:参与者完成事务回滚之后,向协调者发送 ACK 消息。
(4)中断事务:协调者接收到参与者反馈的 ACK 消息之后,执行事务的中断。
三阶段提交的问题:
网络分区可能会带来问题。需要四阶段解决:四阶段直接调用远程服务的数据状态,确定当前数据一致性的情况。