服务器状态

在QuorumPeer中有定义,这个类是一个线程。

  1. LOOKING:寻找Leader状态。处于该状态时,它会认为当前集群中没有Leader,进入选举流程。
  2. FOLLOWING:
  3. LEADING
  4. OBSERVING

选票数据结构

public class Vote {
//
final private int version;
//被选举leader的服务器ID
final private long id;
//被选举leader的事务ID
final private long zxid;
//逻辑时钟,判断多个选票是否处于同一个选举周期,
final private long electionEpoch;
//被推举leader的选举轮次
final private long peerEpoch;
//状态
final private ServerState state;

QuorumCnxManager:网络IO

负责选举leader时的网络通信

消息队列

SendWork和RevWork都是一个线程

    /*
* 分别是发送器,发送队列,最后发送的消息。每个连接都有
*/
final ConcurrentHashMap<Long, SendWorker> senderWorkerMap;//SendWork里面有RevWork对象
final ConcurrentHashMap<Long, ArrayBlockingQueue<ByteBuffer>> queueSendMap;
final ConcurrentHashMap<Long, ByteBuffer> lastMessageSent; /*
* 接受队列只有一个
*/
public final ArrayBlockingQueue<Message> recvQueue;

建立连接

zookeeper为Leader选举会建立一条连接,默认端口是3888。为了防止两台服务器有重复链接,zookeeper定义了规则,只能sid大的去连接sid小的。如果sid小的连接了sid大的,在连接处理程序中会断掉这条连接,然后重新发起连接。

main->receiveConnection->handleConnection(创建sendwork和revwork,并且加入队列集)

消息的接收和发送

  • 消息的接收过程是由消息接收器recvwork负责,它源源不断从TCP读取数据,加入recvQueue(唯一)。

  • 消息发送器主要有两条逻辑

    • 启动sendWork线程后如果发现发送队列是null,从lastMessageSent获取这条数据重新发送。(为了解决由于收到消息前后服务器挂掉,导致消息未正确处理)
    • sendWork从队列queueSendMap里面获取数据,通过调用队列的poll函数从队列获取数据

FastLeaderElection

这是选举选法的核心部分,主要在FastLeaderElection中

选票管理

public class FastLeaderElection implements Election{
//发送队列,用于保存待发送的选票
LinkedBlockingQueue<ToSend> sendqueue;
//接收队列,用于保存接收的外部选票
LinkedBlockingQueue<Notification> recvqueue;
//选票发送器和接收器线程
Messenger messenger; protected class Messenger {
//选票接收器线程,接受选票,如果当前状态不为locking,将leader信息发回
class WorkerReceiver extends ZooKeeperThread{}
//选票发送器线程,发送选票。
//负责把选票转化为消息,放入QuorumCnxManager的发送队列,
//如果是投给自己的,直接放入接收队列
class WorkerSender extends ZooKeeperThread {}
}
}

核心算法——lookForLeader

  • 调用流程:QuorumPeer->locking状态(可以启动只读模式和阻塞模式)->lookForLeader
public Vote lookForLeader() throws InterruptedException {
//...
try {
//用于选票归档
HashMap<Long, Vote> recvset = new HashMap<Long, Vote>(); HashMap<Long, Vote> outofelection = new HashMap<Long, Vote>(); int notTimeout = finalizeWait; synchronized(this){
//自增logicalclock,
logicalclock++;
//初始化选票,投给自己,使用lastProcessedZxid(最后已提交的日志投票)
updateProposal(getInitId(),getInitLastLoggedZxid(),
getPeerEpoch());
} //初始化选票,然后WorkerSender发送
sendNotifications(); /*
* Loop in which we exchange notifications until we find a leader
*/
while ((self.getPeerState() == ServerState.LOOKING) &&
(!stop)){
/*
* Remove next notification from queue, times out after 2 times
* the termination time
*/
Notification n = recvqueue.poll(notTimeout,
TimeUnit.MILLISECONDS); //没有获得外部选票
if(n == null){
//如果连接仍然保持,重新发送投票
if(manager.haveDelivered()){
sendNotifications();
} else {
//连接失效,重新建立连接。开始的时候是这样建立连接的?
manager.connectAll();
}
//修改超时参数...
}
//处理选票
else if(self.getVotingView().containsKey(n.sid)) {
switch (n.state) {
case LOOKING:
// 大于当前选举轮次
if (n.electionEpoch > logicalclock) {
logicalclock = n.electionEpoch;
//清空接受的选票
recvset.clear();
//选票PK,外部更新。有3条规则
if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) {
//变更选票
updateProposal(n.leader, n.zxid, n.peerEpoch);
} else {
//不变更选票
updateProposal(getInitId(),
getInitLastLoggedZxid(),
getPeerEpoch());
}
sendNotifications();
}
// 小于当前选举轮次,直接丢弃
else if (n.electionEpoch < logicalclock) {
break;
}
//等于当前选举轮次,直接PK
else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
proposedLeader, proposedZxid, proposedEpoch)) {
updateProposal(n.leader, n.zxid, n.peerEpoch);
sendNotifications();
} //无论是否重新投票,都要选票归档,<sid, 选票>
//都是和自己的提议对比
recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); //统计投票,决定是否终止投票
if (termPredicate(recvset,
new Vote(proposedLeader, proposedZxid,
logicalclock, proposedEpoch))) { // 判断leader是否改变
while((n = recvqueue.poll(finalizeWait,
TimeUnit.MILLISECONDS)) != null){
if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
proposedLeader, proposedZxid, proposedEpoch)){
recvqueue.put(n);
break;
}
} if (n == null) {
//设置状态,如果leader是自己,状态为Leading
//如果leader是其他节点,状态可能为observing或者following
self.setPeerState((proposedLeader == self.getId()) ?
ServerState.LEADING: learningState()); Vote endVote = new Vote(proposedLeader,
proposedZxid,
logicalclock,
proposedEpoch);
//清空接收队列
leaveInstance(endVote);
return endVote;
}
}
break;
case OBSERVING:
break;
//已经选出结果
case FOLLOWING:
case LEADING:
//除了做出过半判断,同时还要检查leader是否给自己发送过投票信息,从投票信息中确认该leader是不是LEADING状态(防止出现时间差)。 /* 同一轮投票选出leader,那么判断是不是半数以上的服务器都选举同一个leader,如果是设置角色并退出选举 */
if(n.electionEpoch == logicalclock){
recvset.put(n.sid, new Vote(n.leader,
n.zxid,
n.electionEpoch,
n.peerEpoch)); if(ooePredicate(recvset, outofelection, n)) {
self.setPeerState((n.leader == self.getId()) ?
ServerState.LEADING: learningState()); Vote endVote = new Vote(n.leader,
n.zxid,
n.electionEpoch,
n.peerEpoch);
leaveInstance(endVote);
return endVote;
}
} /* 非同一轮次,例如宕机很久的机器重新启动/某个节点延迟很大变为locking,需要收集过半选票。*/
outofelection.put(n.sid, new Vote(n.version,
n.leader,
n.zxid,
n.electionEpoch,
n.peerEpoch,
n.state)); if(ooePredicate(outofelection, outofelection, n)) {
synchronized(this){
logicalclock = n.electionEpoch;
self.setPeerState((n.leader == self.getId()) ?
ServerState.LEADING: learningState());
}
Vote endVote = new Vote(n.leader,
n.zxid,
n.electionEpoch,
n.peerEpoch);
leaveInstance(endVote);
return endVote;
}
break;
default:
break;
}
} else {
LOG.warn("Ignoring notification from non-cluster member " + n.sid);
}
}
return null;
}
}
  • 初始选票

    • (sid, LastLoggedZxid, currentEpoch)
    • LastLoggedZxid为处理(包括提交,未提交)
  • 接收到新的选票后,从以下几个层次判断
    • 选票状态
    • 选票轮次
    • 选票变更规则
  • 变更选票的3条规则
    • New epoch更高
    • epoch相同,选择zxid更高的
    • 前面的都相同,选择sid更高的

模块图总结

zookeeper leader选举算法源码的更多相关文章

  1. zookeeper集群搭建及Leader选举算法源码解析

    第一章.zookeeper概述 一.zookeeper 简介 zookeeper 是一个开源的分布式应用程序协调服务器,是 Hadoop 的重要组件. zooKeeper 是一个分布式的,开放源码的分 ...

  2. zookeeper系列之五—Leader选举算法

    leader选举算法 zookeeper server内部原理 zookeeper client

  3. Zookeeper——分布式一致性协议及Zookeeper Leader选举原理

    文章目录 一.引言 二.从ACID到CAP/BASE 三.分布式一致性协议 1. 2PC和3PC 2PC 发起事务请求 事务提交/回滚 3PC canCommit preCommit doCommit ...

  4. Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结

    Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结 1.1. 原理,主要使用像素模糊后的差别会变小1 1.2. 具体流程1 1.3. 提升性能 可以使用采样法即可..1 ...

  5. mahout算法源码分析之Collaborative Filtering with ALS-WR (四)评价和推荐

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 首先来总结一下 mahout算法源码分析之Collaborative Filtering with AL ...

  6. mahout算法源码分析之Collaborative Filtering with ALS-WR拓展篇

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 额,好吧,心头的一块石头总算是放下了.关于Collaborative Filtering with AL ...

  7. mahout算法源码分析之Collaborative Filtering with ALS-WR 并行思路

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. mahout算法源码分析之Collaborative Filtering with ALS-WR 这个算 ...

  8. 设置ZooKeeper服务器地址列表源码解析及扩展

    设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...

  9. diff.js 列表对比算法 源码分析

    diff.js列表对比算法 源码分析 npm上的代码可以查看 (https://www.npmjs.com/package/list-diff2) 源码如下: /** * * @param {Arra ...

随机推荐

  1. Bandit Wargame Level12 Writeup

    Level Goal The password for the next level is stored in the file data.txt, which is a hexdump of a f ...

  2. 前端开发JavaScript清除浏览器缓存的方法

    查看和删除浏览器缓存的方法=====>打开 最近在开发项目中发现有时候总要频繁地清除浏览器缓存,不然总是显示的过时的信息 浏览器缓存有利有弊,有些数据需要缓存下来使得页面打开更快提高网站性能,但 ...

  3. Freemarker页面静态化技术,activemq监听页面变动

    初步理解: 架构优化: 静态页面的访问速度优于从缓存获取数据的动态页面的访问速度: Freemarker: 导包 模板:hello.ftl <!DOCTYPE html> <html ...

  4. 《TCP-IP详解卷2:实现》【PDF】下载

    <TCP-IP详解卷2:实现>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062539 内容简介 <TCP/IP详解·卷2 ...

  5. Layui常见问题

    为什么表单不显示?当你使用表单时,Layui会对select.checkbox.radio等原始元素隐藏,从而进行美化修饰处理.但这需要依赖于form组件,所以你必须加载 form,并且执行一个实例. ...

  6. iOS 视频开发学习

    原文:浅谈iOS视频开发 这段时间对视频开发进行了一些了解,在这里和大家分享一下我自己觉得学习步骤和资料,希望对那些对视频感兴趣的朋友有些帮助. 一.iOS系统自带播放器 要了解iOS视频开发,首先我 ...

  7. [array] leetcode - 33. Search in Rotated Sorted Array - Medium

    leetcode - 33. Search in Rotated Sorted Array - Medium descrition Suppose an array sorted in ascendi ...

  8. 使用JavaScript将图片保存至本地

    在最近的开发当中,我们需要为img标签以及canvas动态绘制的图像提供下载功能,下面是经过探索后我们得出的结果. 一.Canvas 版本 // 下载Canvas元素的图片 function down ...

  9. java进程脱离终端运行

    关于 java 进程脱离终端放入后台运行的问题,首先想到是使用nohup命令: 研究了一下tomcat启动脚本.jenkins.war启动方式以及其他linux命令,结论是在目前的linux系统上不使 ...

  10. Thomas Hobbes: Leviathan

    Man is distinguished, not only by his reason, but by this singular passion from other animals, which ...