前⾔
Zab(Zookeeper Atomic Broadcast)是为ZooKeeper协设计的崩溃恢复原⼦⼴播协议,它保证zookeeper集群数据的⼀致性和命令的全局有序性。
概念介绍
在介绍zab协议之前⾸先要知道zookeeper相关的⼏个概念,才能更好的了解zab协议。
集群⻆⾊
- Leader:同⼀时间集群总只允许有⼀个Leader,提供对客户端的读写功能,负责将数据同步⾄各个节点;
- Follower:提供对客户端读功能,写请求则转发给Leader处理,当Leader崩溃失联之后参与Leader选举;
- 3.server:与Follower不同的是但不参与Leader选举。
服务状态
- LOOKING:当节点认为群集中没有Leader,服务器会进⼊LOOKING状态,⽬的是为了查找或者选举Leader;
- FOLLOWING:follower⻆⾊;
- LEADING:leader⻆⾊;
- OBSERVING:observer⻆⾊; 可以知道Zookeeper是通过⾃身的状态来区分⾃⼰所属的⻆⾊,来执⾏⾃⼰应该的任务。
- ZAB状态 Zookeeper还给ZAB定义的4中状态,反应Zookeeper从选举到对外提供服务的过程中的四个步骤。 状态枚举定义:
public enum ZabState {
ELECTION,
DISCOVERY,
SYNCHRONIZATION,
BROADCAST
}
- ELECTION: 集群进⼊选举状态,此过程会选出⼀个节点作为leader⻆⾊;
- DISCOVERY:连接上leader,响应leader⼼跳,并且检测leader的⻆⾊是否更改,通过此步骤之后选举出的leader才能执⾏真正职务;
- SYNCHRONIZATION:整个集群都确认leader之后,将会把leader的数据同步到各个节点,保证整个集群的数据⼀致性;
- BROADCAST:过渡到⼴播状态,集群开始对外提供服务。
ZXID
Zxid是极为重要的概念,它是⼀个long型(64位)整数,分为两部分:纪元(epoch)部分和计数器(counter)部分,是⼀个全局有序的数字。
epoch代表当前集群所属的哪个leader,leader的选举就类似⼀个朝代的更替,你前朝的剑不能斩本朝的官,⽤epoch代表当前命令的有效性,counter是⼀个递增的数字。
选举
进⾏leader有三个问题,什么时候进⾏?选举规则?选择流程?
- 选举发⽣的时机 Leader发⽣选举有两个时机,⼀个是服务启动的时候当整个集群都没有leader节点会进⼊选举状态,如果leader已经存在就会告诉该节点leader的信息,⾃⼰连接上leader,整个集群不⽤进⼊选举状态。还有⼀个就是在服务运⾏中,可能会出现各种情况,服务宕机、断电、⽹络延迟很⾼的时候leader都不能再对外提供服务了,所有当其他⼏点通过⼼跳检测到leader失联之后,集群也会进⼊选举状态。
- 选举规则 进⼊投票选举流程,怎么才能选举出leader?或者说按照什么规则来让其他节点都能选举你当leader。
- zab协议是按照⼏个⽐较规则来进⾏投票的筛选,如果你的票⽐我更好,就修改⾃身的投票信息,改投你当leader。 下⾯代码是zookeeper投票⽐较规则:
/*
* We return true if one of the following three cases hold:
* 1- New epoch is higher
* 2- New epoch is the same as current epoch, but new zxid is higher
* 3- New epoch is the same as current epoch, new zxid is the same
* as current zxid, but server id is higher.
*/
return ((newEpoch > curEpoch)
|| ((newEpoch == curEpoch)
&& ((newZxid > curZxid)
|| ((newZxid == curZxid)
&& (newId > curId)))));
当其他节点的纪元⽐⾃身⾼投它,如果纪元相同⽐较⾃身的zxid的⼤⼩,选举zxid⼤的节点,这⾥的zxid代表节点所提交事务最⼤的id,zxid越⼤代表该节点的数据越完整。最后如果epoch和zxid都相等,则⽐较服务的serverId,这个Id是配置zookeeper集群所配置的,所以我们配置zookeeper集群的时候可以把服务性能更⾼的集群的serverId配置⼤些,让性能好的机器担任leader⻆⾊。
3. 选举流程
时机和规则都有了,下⾯就是leader的选举流程:
- 所有节点第⼀票先选举⾃⼰当leader,将投票信息⼴播出去;
- 从队列中接受投票信息;
- 按照规则判断是否需要更改投票信息,将更改后的投票信息再次⼴播出去;
- 判断是否有超过⼀半的投票选举同⼀个节点,如果是选举结束根据投票结果设置⾃⼰的服务状态,选举结束,否则继续进⼊投票流程。
4. 举例
⼴播
集群在经过leader选举之后还会有连接leader和同步两个步骤,这⾥就不具体分析这两个步骤的流程了,主要介绍集群对外提供服务如何保证各个节点数据的⼀致性。
zab在⼴播状态中保证以下特征
- 可靠传递: 如果消息m由⼀台服务器传递,那么它最终将由所有服务器传递。
- 全局有序: 如果⼀个消息a在消息b之前被⼀台服务器交付,那么所有服务器都交付了a和b,并且a先于b。
- 因果有序: 如果消息a在因果上先于消息b并且⼆者都被交付,那么a必须排在b之前。
有序性是zab协议必须要保证的⼀个很重要的属性,因为zookeeper是以类似⽬录结构的数据结构存储数据的,必须要求命名的有序性。
⽐如⼀个命名a创建路径为/test,然后命名b创建路径为/test/123,如果不能保证有序性b命名在a之前,b命令会因为⽗节点不存在⽽创建失败。如上图所示,整个写请求类似⼀个⼆阶段的提交。
当收到客户端的写请求的时候会经历以下⼏个步骤:
- Leader收到客户端的写请求,⽣成⼀个事务(Proposal),其中包含了zxid;
- Leader开始⼴播该事务,需要注意的是所有节点的通讯都是由⼀个FIFO的队列维护的;
- Follower接受到事务之后,将事务写⼊本地磁盘,写⼊成功之后返回Leader⼀个ACK;
- Leader收到过半的ACK之后,开始提交本事务,并⼴播事务提交信息
- 从节点开始提交本事务。
有以上流程可知,zookeeper通过⼆阶段提交来保证集群中数据的⼀致性,因为只需要收到过半的ACK就可以提交事务,所以zookeeper的数据并不是强⼀致性。
zab协议的有序性保证是通过⼏个⽅⾯来体现的,第⼀是,服务之前⽤TCP协议进⾏通讯,保证在⽹络传输中的有序性;第⼆,节点之前都维护了⼀个FIFO的队列,保证全局有序性;第三,通过全局递增的zxid保证因果有序性。
状态流转
前⾯介绍了zookeeper服务状态有四种,ZAB状态也有四种。这⾥就简单介绍⼀个他们之间的状态流转,更能加深对zab协议在zookeeper⼯作流程中的作⽤。
- 服务在启动或者和leader失联之后服务状态转为LOOKING;
- 如果leader不存在选举leader,如果存在直接连接leader,此时zab协议状态为ELECTION;
- 如果有超过半数的投票选择同⼀台server,则leader选举结束,被选举为leader的server服务状态为LEADING,其他server服务状态为FOLLOWING/OBSERVING;
- 所有server连接上leader,此时zab协议状态为DISCOVERY;
- leader同步数据给learner,使各个从节点数据和leader保持⼀致,此时zab协议状态为SYNCHRONIZATION;
- 同步超过⼀半的server之后,集群对外提供服务,此时zab状态为BROADCAST。可以知道整个zookeeper服务的⼯作流程类似⼀个状态机的转换,⽽zab协议就是驱动服务状态流转的关键,理解了zab就理解了zookeeper⼯作的关键原理 可以知道整个zookeeper服务的⼯作流程类似⼀个状态机的转换,⽽zab协议就是驱动服务状态流转的关键,理解了zab就理解了zookeeper⼯作的关键原理