ZAB 协议,全称 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议),是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举:(1)服务器初始化启动;(2)服务器运行期间 Leader 故障。 ~ 本篇内容包括:关于 ZAB 协议、Zookeeper 选主时机、Zookeeper 选主机制。
文章目录- 一、关于 ZAB 协议
- 1、ZAB 协议简述
- 2、ZooKeeper 集群中的三个服务器角色
- 1、ZAB 协议简述
- 2、ZooKeeper 集群中的三个服务器角色
- 二、Zookeeper 选主时机
- 1、ZooKeeper 服务器的工作状态
- 2、Zookeeper 选主时机
- 3、FOLLOWING 状态节点(Follower)的主流程
- 4、LOOKING 状态节点的主流程
- 5、LEADING 状态节点(Leader)的主流程
- 三、Zookeeper 选主机制
- 1、涉及到的相关概念
- 2、Zookeeper 选举流程
- 3、集团初始选举
- 4、集群重新选举
- 5、选举流程总结
一、关于 ZAB 协议
1、ZAB 协议简述
ZAB 协议,全称 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议),是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
作为分布式共识算法的一员,Zab 算法构成了著名的 ZooKeeper 的基石。与赫赫有名的 Paxos、Raft 一样,Zab 算法也提供了强一致性的保证。
从设计上看,ZAB 协议和 Raft 很类似。ZooKeeper 集群中,只有一个 Leader 节点,其余均为 Follower 节点。
2、ZooKeeper 集群中的三个服务器角色
Zookeeper 集群中的机器分为以下三种角色:
- Leader:①、整个 Zookeeper 集群工作机制中的核心,过选举产生的集群领导者,提供读写服务;②、一个 Zookeeper 集群中同一时间只能有一个实际工作的 Leader,它用来维护各个 Follow 与 Observer 之间的心跳;③、Leader 是事务请求的唯一调度和处理者,Follow 接收到事务请求会将请求转发给 Leader 处理。
- Follow:①、Follow 只提供读服务,即只处理非事务请求,它接收到事务请求会转发给 Leader 服务器;②、它参与 Leader 的选举,参与事务请求 Proposal 的投票;③、一个 Zookeeper 集群同时可以有多个 Follow。
- Observer:①、功能和 Follow 基本一致,提供读服务,即只处理非事务请求,唯一的差别是不参与任何投票(包括事务请求 Proposal 和 Leader 的选举);②、Observer 的作用主要就是在不影响集群事务处理前提下提升集群的非事务处理。
二、Zookeeper 选主时机
1、ZooKeeper 服务器的工作状态
ZooKeeper 服务器有四种工作状态:
- LOOKING:竞选状态,寻找 Leader。当服务器处于该状态时,它会认为当前服务器没有 Leader,因此需要进入 Leader 选举状态。
- FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
- LEADING:领导者状态。表明当前服务器角色是 Leader。
- OBSERVING:观察者状态。表明当前服务器角色是 Observer。
2、Zookeeper 选主时机
当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举:(1)服务器初始化启动;(2)服务器运行期间 Leader 故障。
- 服务器初始化启动:每个节点启动的时候状态都是 LOOKING,处于观望状态,接下来就是要进行选主了。
- 服务器运行期间 Leader 故障:Leader 节点运行后会周期性地向 Follower 发送心跳信息(称之为 ping)
- 如果一个 Follower 未收到 Leader 节点的心跳信息,Follower 节点的状态会从 FOLLOWING 转变为 LOOKING;
- Leader 节点也会检测 Follower 节点的状态,如果多数 Follower 节点不再响应 Leader 节点(可能是 Leader 节点与 Follower 节点之间产生了网络分区),那么 Leader 节点可能此时也不再是合法的 Leader 了,也必须要进行一次新的选主。
3、FOLLOWING 状态节点(Follower)的主流程
FOLLOWING 状态节点(Follower)的主流程:
代码语言:javascript复制void followLeader() throws InterruptedException {
try {
......
while (this.isRunning()) {
readPacket(qp);
processPacket(qp);
}
// 如果上面的 while 循环内出现异常
// Ps:长时间没有收到 Leader 的消息也是异常
} catch (Exception e) {
// 出现异常就退出了 while 循环
// 也就结束了 Follower 的处理流程
}
4、LOOKING 状态节点的主流程
LOOKING 状态节点的主流程:
代码语言:javascript复制public void run() {
while (running) {
switch (getPeerState()) {
case FOLLOWING:
try {
setFollower(makeFollower(logFactory));
follower.followLeader();
} catch (Exception e) {
......
} finally {
follower.shutdown();
setFollower(null);
// 状态更新为 LOOKING
updateServerState();
}
break;
......
}
}
5、LEADING 状态节点(Leader)的主流程
在 Leader 节点的主循环流程中,会判断多数派节点的消息状态,如下:
代码语言:javascript复制void lead() throws IOException, InterruptedException {
......
while (true) {
......
// 判断每个每个 Follower 节点的状态
// 是否与 Leader 保持同步
for (LearnerHandler f : getLearners()) {
if (f.synced()) {
syncedAckSet.addAck(f.getSid());
}
}
......
}
if (!tickSkip && !syncedAckSet.hasAllQuorums()) {
// 如果失去了大多数 Follower 节点的认可,就跳出 Leader 主循环,进入选主流程
break;
}
......
}
// LearnerHandler::synced() 逻辑
// 即判断当前是否已经过了期望得到的 Follower 的下一个消息的期限:tickOfNextAckDeadline
public boolean synced() {
return isAlive() && leader.self.tick.get() <= tickOfNextAckDeadline;
}
三、Zookeeper 选主机制
1、涉及到的相关概念
# Server id(myid 或 sid):服务器 ID
比如有三台服务器,编号分别是 1,2,3。编号越大在选择算法中的权重越大,比如初始化启动时就是根据服务器 ID 进行比较。
# Zxid:事务ID
服务器中存放的数据的事务 ID,值越大说明数据越新,在选举算法中数据越新权重越大。
zxid 有两部分组成:高 32位 是 epoch,低 32位 是 epoch 内的自增 id,由 0 开始。每次选出新的 Leader,epoch 会递增,同时 zxid 的低 32 位清 0
# Epoch:逻辑时钟
也叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的,每投完一次票这个数据就会增加。
2、Zookeeper 选举流程
选举大致流程:
- 初始投票:服务器启动后,每个 Server 都会给自己投上一票,每次投票会包含所投票服务器的 myid 和 zxid
- 同步投票结果:集群中的服务器在投票后,会将各自的投票结果同步给集群中其他服务器。
- 检查投票有效性:各服务器在收到投票后会检查投票的有效性,如:是否本轮投票,是否来自 LOOKING 状态的服务器的投票等。
- 处理投票:服务器之间会进行投票比对,规则如下:①、优先检查 zxid,较大的服务器优先作为 Leader;②、如果 zxid 相同,则 myid 较大的服务器作为 Leader;
- 统计投票结果:每轮投票比对之后都会统计投票结果,确认是否有超过半数的机器都得到相同的投票结果,如果是,则选出 Leader,否则继续投票。
- 更改服务器状态:一旦选出 Leader,每个服务器就会各自更新自己的状态
3、集团初始选举
假设我们有服务器 1~5,服务器初始化启动选主流程(粗略版):
- 「服务器1」启动,发起一次选举。「服务器1」投自己一票。此时「服务器1」票数一票,不够半数以上(3票),选举无法完成,「服务器1」状态保持为 LOOKING;
- 「服务器2」启动,再发起一次选举。「服务器1」和「服务器2」分别投自己一票并交换选票信息;此时「服务器1」发现「服务器2」的 myid 比自己目前投票推举的「服务器1」大,更改选票为推举「服务器2」。此时「服务器1」票数 0 票,服「务器2」票数 2 票,没有半数以上结果,选举无法完成,「服务器1」,「服务器2」保持 LOOKING;
- 「服务器3」启动,发起一次选举。此时「服务器1」和「服务器2」都会更改选票为「服务器3」。此次投票结果:「服务器1」、「服务器2」为 0。票,「服务器3」为 3。票。此时「服务器3」的票数已经超过半数,「服务器3」当选成为 Leader。「服务器1」、「服务器2」更改状态为 FOLLOWING,「服务器3」更改状态为 LEADING;
- 「服务器4」启动,发起一次选举。此时「服务器1,2,3」已经不是 LOOKING 状态,不会更改选票信息。交换选票信息结果:「服务器3」为 3 票,「服务器4」为 1 票。此时「服务器4」服从多数,更改选票信息为「服务器3」,并更改状态为 FOLLOWING;
- 「服务器5」同「服务器4」。
4、集群重新选举
Zookeeper 集群运行期间无法和 Leader 保持正常连接时,即如果 Leader 挂了,或者 Leader 服务器故障都会进行新一轮的 Leader 选举。需要重新选举时,选举过程就需要加入数据 id,服务器id,和逻辑时钟。
集群重新选举时,根据 myid 和 zxid 的大小共同决断,zxid 更大的优先成为 Leader,选举的标准(粗略版):
- 逻辑时钟小的选举结果会被忽略,重新投票;
- 统一逻辑时钟后,事务 id 大的胜出,当选 Leader;
- 事务 id 相同的情况下,服务器 id 大的胜出,当选 Leader。
5、选举流程总结
总结:Zookeeper 集群按 myid 从小到大依次启动初始化时,在超过半数机器的投票的情况下,谁的 myid 最后,谁就是 Leader,知道这个定律,不同的集群规模我们都可以推算出谁是 Leader。
# 集群初始化时:
- 集群有 3 台机器,第 2 大的 myid 所在服务器就是 Leader
- 集群有 4 台机器,第 3 大的 myid 所在服务器就是 Leader;
- 集群有 5 台机器,第 3 大的 myid 所在服务器就是 Leader;
- 集群有 6 台机器,第 4 大的 myid 所在服务器就是 Leader;
集群重新选举时,根据 myid 和 zxid 的大小共同决断,zxid 更大的优先成为 Leader。