为了保证写操作的一致性,zookeeper专门设计了原子广播协议(zab,zookeeper atomic broadcast),通过这种协议以及zookeeper的主从架构模式,来保证集群中各个副本之间的数据一致性。
1. zab协议理论
先来介绍一下单纯zab协议的理论,首先是zab协议中会给各个服务器定义不同的状态,分别为:
- Looking:表示当前集群没有Leader节点,进入领导选举过程
- Leading:节点成为Leader后,该节点的状态
- Following:所有Follower节点的状态
然后,对于zab协议主要分为四个节点,分别是选举阶段、发现阶段、同步阶段以及广播阶段,接下来分别介绍每个状态。
1.1 选举节点
集群刚启动(或者主节点宕机)的时候,所有节点的状态其实都是Looking的,此时集群处于选举阶段,选举阶段需要有超过集群中一半以上节点的支持,某个节点才能被选择为准Leader,这里说是准Leader是因为这个阶段选举出来的节点还需要进行后续的同步工作成功之后,才能真正作为Leader来提供服务。
1.2 发现阶段
在选举阶段选出Leader后,进入发现阶段,这个阶段主要要做的事情是同步集群中所有节点的状态,使得Leader中有集群中所有待提交以及未提交的事务的状态,即Leader中的状态是最新的状态。
1.3 同步阶段
在Leader状态更新到最新后,会把所有状态同步到集群所有节点中,只有同步完成时,准Leader才能正式成为Leader,这个过程其实就是事务提交并commit的过程。
1.4 广播阶段
到了这个阶段,zookeeper就可以真正对外提供事务服务,leader接收各种新的写请求,然后进行消息广播。
2. zookeeper中zab协议的实现
理论实现和具体代码实现还是有一些区别的,zab实现只有三个阶段:选举阶段、恢复阶段、广播阶段,其中恢复阶段就是发现阶段和同步阶段的合并。
2.1 选举阶段
服务选举阶段中,要求服务器两两之间建立连接,我们在安装zookeeper集群的时候,会需要为每台节点配置一个myid整数,这个myid配置的就是节点的编号,为了防止A和B建立连接之后,B再重复去找A建立连接,选举阶段建立连接的过程只允许myid大的节点主动去找myid小的节点建立连接。
具体选举准Leader的算法有多种,默认的是FastLeaderElection,其它几种算法已经废弃。
FastLeaderElection原理
算法原理主要涉及到几个关键词:
- logicClock(epoch):记录当前是第几轮投票,依次递增,logicClock小的选票优先级小于logicClock大的选票
- zxid: 这是64位全局唯一的自增id,Leader接收到消息的时候会给消息赋予这个全局唯一的zxid,它的高32位记录的是Leader的epoch,低32位为当前这个epoch轮下的消息计数器,每次重新选举Leader的时候,最终得到的Leader中会新生成一个zxid,高32位会+1,低32位会重置为0。
- myid:前面说的配置的节点编号
通过FastLeaderElection选举Leader的过程简单来说就是通过比较logicClock,再比较zxid的情况下,找到最新一轮选举logicClock下,哪台节点中有最新的zxid,因为其实不论是leader还是follower,它们的数据状态目的都是要尽量保持一致的,leader会把状态同步到follower,在恢复的过程中其实就是需要找到一台数据是最新的节点,这样在进行恢复的过程中以该节点为基础同步数据到其它节点会相对较快,当然有可能会有多台节点都持有最新的状态,那么此时就比较配置的节点编号myid,选举相同节点myid最大的作为准Leader,具体原理流程参考:https://dbaplus.cn/news-141-1875-1.html。
2.2 恢复阶段
选举阶段已经选择好了持有最新状态的节点,恢复阶段要做的事情就是把准Leader的状态给同步到所有子节点中,因为Leader宕机的时候可能会有部分事务未得到半数以上follower的ack,所以没有commit,这个过程主要是保证所有事务均commit成功。
2.3 广播阶段
这个过程Leader已经同步数据完毕,正式成为Leader,接收外部请求。这里要知道一点就是zab协议要求所有写操作全部由Leader完成,Leader完成写操作之后,再广播到各个Follower子节点中。
参考文章:
https://segmentfault.com/a/1190000014932133
https://dbaplus.cn/news-141-1875-1.html