分布式与一致性协议之ZAB协议(四)

ZAB协议

ZooKeeper是如何选举领导者的。

首先我们来看看ZooKeeper是如何实现成员身份的?

在ZooKeeper中,成员状态是在QuorumPeer.java中实现的,为枚举型变量

java 复制代码
public enum ServerState {
LOOKING,
FOLLOWING,
LEADING,
OBSERVING
}

其实,ZooKeeper没有直接定义成员身份,而是用了对应的成员状态来表示,比如,处于FOLLOWING状态的节点为跟随者。如果你想研究相关成员的功能和实现,那么可以把对应的成员状态作为切入点来研究。比如,你想研究领导者的功能实现,可以在代码中搜索LEADING关键字,然后研究相应的上下文逻辑,进而得到自己想要的答案。

如果跟随者将自己的状态从跟随者状态变更为选举状态,就表示跟随者在发起领导者选举,那么在ZooKeeper中,领导者选举是如何实现的呢?

领导者选举是在FastLeaderElection.lookForLeader()中实现的。其核心实现流程如图所示。

为了更好地理解这个流程,我们来一起走读下核心代码:

  • 1.在集群稳定运行时,处于跟随者状态的节点会调用Follower.followLeader()函数周期性地读数据包和处理数据包,如代码所示
java 复制代码
QuorumPacket qp = new QuorumPacket();
while (this.isRunning()) {
//读取数据包
readPacket(qp);
// 处理数据包
processPacket(qp);
}
  • 2.当跟随者检测到连接到领导者的读操作超时时(比如领导者节点故障了),它会抛出异常(Exception),跳出上面的读取数据保和处理数据保的循环,并将节点状态变更为选举状态。如代码所示
java 复制代码
public void run() {
case FOLLOWING:
......
finally {
// 关闭跟随者节点
follower.shutdown();
setFollower(null);
// 设置状态为选举状态
updateServerState();
}
break;
......
}
  • 3.当节点处于选举状态时,它将调用makeLEStrategy().lookForLeader()函数(实际对应的函数为FastLeaderElection.lookForLeader())发起领导者选举,如代码所示
java 复制代码
setCurrentVote(makeLEStrategy().lookForLeader());
  • 4.在FastLeaderElection.lookForLeader()函数中,节点需要对逻辑时钟(也就是选举的轮次)的值执行加1操作,表示开启一轮新的领导者选举,然后创建投票提案(默认推荐自己为领导者)并通知所有节点,如代码所示
java 复制代码
synchronized(this) {
// 对逻辑时钟的值执行加一操作
logicalclock.incrementAndGet();
// 创建投票提案,并默认推荐自己为领导者
updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
}
// 广播投票信息给所有节点
sendNotifications();
  • 5.当节点处于选举状态时,它会周期性地从队列中读取接收到地投票信息,直到选举成功,如代码所示
java 复制代码
while((self.getPeerState() == ServerState.LOOKING) && (!stop)) {
// 从队列中读取接收到地投票信息
Notification n = recvqueue.poll(notTimeout, TimeUnit.MILLISECONDS);
......
}
  • 6.当接收到新的投票信息时,节点会进行领导者PK,来判断谁更适合当领导者。如果投票信息中提议的节点比自己提议的节点更适合作为领导者,
    则该节点会更新投票信息,推荐投票信息中提议的节点作为领导者,并广播给所有节点,如代码所示
java 复制代码
else if (totalOrderPredicate(n.leader, n.zxid,n.peerEpoch,proposedLeader, proposedZxid, proposedEpoch)) {
// 如果投票信息中提议的节点比自己提议的节点更适合作为领导者,则更新投票信息
// 并推荐投票信息中提议的节点
updateProposal(n.leader,n.zxid,n.peerEpoch);
// 将新的投票信息广播给所有节点
sendNotifications();
}
  • 7.如果自己提议的领导者赢得大多数选票,则执行步骤8,变更节点状态,退出选举,如果自己提议的领导者仍未赢得大多数选票,则执行步骤5,继续从接收队列中读取新的投票信息。
  • 8.最后,当节点提议的领导者赢得大多数选票时,则节点会根据投票结果,判断并变更节点状态(如变更为领导者或跟随者),然后退出选举,如代码所示
java 复制代码
if (voteSet.hasAllQuorums()) {
......
// 根据投票结果,判断并设置节点状态
setPeerState(propsedLeader, voteSet);
// 退出领导者选举
Vote endVote = new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch);
leaveInstance(endVote);
return endVote;
......
}

注意

这里只是演示了一种选举情况,还有更多情况需要实践,比如接收到来自逻辑时钟的值比当前节点的值小的节点的投票哦信息,再比如接收到来自领导者的投票信息

如何从故障中恢复

在前面我们提到了ZAB协议的领导者选举,在我看来,它只是选举了一个适合当领导者的节点,然后把这个节点的状态设置成LEAEDING状态。此时,这个节点还不能作为主节点处理写请求,也不能使用领导职能(比如,它没办法阻止其他"领导者"广播提案)。也就是说,集群还没有从故障中恢复过来,而成员发现和数据同步会解决这个问题。

总的来说,成员发现和数据不同不仅让新领导者正式成为领导者,确立了它的领导关系,还解决了个副本数据冲突的问题,实现了数据副本的一致性,使集群能够正常处理写请求,这里需要注意的是:

  • 1.确立领导关系是指在成员发现(DISCOVERY)阶段,领导者和大多数跟随者建立连接,并再次确认各节点对自己当选领导者没有异议,从而确立自己的领导关系
  • 2.处理冲突数据是指在数据同步(SYNCHRONIZATION)阶段,领导者以自己的数据为准,解决各节点数据副本不一致的问题。
    理解这两点,有助于更好地理解ZooKeeper如何恢复故障,以及当主节点崩溃时,哪些数据会丢失、哪些数据不会丢失的原因等。换句话说,通过上述内容,我们能更好地理解ZooKeeper的节点故障容错能力
相关推荐
小趴菜不能喝1 分钟前
spring boot 3.x 整合Swagger3
java·spring boot·swagger
一叶飘零_sweeeet11 分钟前
Dubbo 构建高效分布式服务架构
分布式·架构·dubbo
微服务技术分享14 分钟前
专为成长型企业打造的Java CRM系统源码:CRM客户关系管理系统技术解析与功能构建
java·crm客户关系管理系统源码·鸿鹄crm客户关系管理系统·鸿鹄crm客户关系管理系统源码
琪露诺大湿14 分钟前
JavaEE-多线程初阶(4)
java·开发语言·jvm·java-ee·基础·1024程序员节·原神
Java程序员-小白23 分钟前
Spring Shell——快速构建终端应用,自定义终端命令
java·后端·spring
想做白天梦28 分钟前
LeetCode :150. 逆波兰表达式求值(含求后缀表达式和中缀转后缀表达式)
java·前端·算法
远望樱花兔32 分钟前
【d63】【Java】【力扣】141.训练计划III
java·开发语言·leetcode
九圣残炎34 分钟前
【从零开始的LeetCode-算法】3254. 长度为 K 的子数组的能量值 I
java·算法·leetcode
孤蓬&听雨36 分钟前
RabbitMQ自动发送消息工具(自动化测试RabbitMQ)
分布式·测试工具·自动化·rabbitmq·自动发送消息
呼啦啦啦啦啦啦啦啦36 分钟前
RabbitMQ 七种工作模式介绍
分布式·rabbitmq