zookeeper leader选举算法源码
服务器状态
在QuorumPeer中有定义,这个类是一个线程。
- LOOKING:寻找Leader状态。处于该状态时,它会认为当前集群中没有Leader,进入选举流程。
- FOLLOWING:
- LEADING
- 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选举算法源码的更多相关文章
- zookeeper集群搭建及Leader选举算法源码解析
第一章.zookeeper概述 一.zookeeper 简介 zookeeper 是一个开源的分布式应用程序协调服务器,是 Hadoop 的重要组件. zooKeeper 是一个分布式的,开放源码的分 ...
- zookeeper系列之五—Leader选举算法
leader选举算法 zookeeper server内部原理 zookeeper client
- Zookeeper——分布式一致性协议及Zookeeper Leader选举原理
文章目录 一.引言 二.从ACID到CAP/BASE 三.分布式一致性协议 1. 2PC和3PC 2PC 发起事务请求 事务提交/回滚 3PC canCommit preCommit doCommit ...
- Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结
Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结 1.1. 原理,主要使用像素模糊后的差别会变小1 1.2. 具体流程1 1.3. 提升性能 可以使用采样法即可..1 ...
- mahout算法源码分析之Collaborative Filtering with ALS-WR (四)评价和推荐
Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 首先来总结一下 mahout算法源码分析之Collaborative Filtering with AL ...
- mahout算法源码分析之Collaborative Filtering with ALS-WR拓展篇
Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 额,好吧,心头的一块石头总算是放下了.关于Collaborative Filtering with AL ...
- 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 这个算 ...
- 设置ZooKeeper服务器地址列表源码解析及扩展
设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...
- diff.js 列表对比算法 源码分析
diff.js列表对比算法 源码分析 npm上的代码可以查看 (https://www.npmjs.com/package/list-diff2) 源码如下: /** * * @param {Arra ...
随机推荐
- 前端笔记----类型转换display
display属性用来在行内元素,块元素,行内块元素之间进行转化. 常用的属性有: 1.none :元素隐藏且不占位置,相当于不存在,一般用在动态展示效果:2.block :元素以块元素显示,有些行内 ...
- thymeleaf模板的使用(转)
作者:纯洁的微笑 出处:http://www.ityouknow.com/ 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thym ...
- 八.利用springAMQP实现异步消息队列的日志管理
经过前段时间的学习和铺垫,已经对spring amqp有了大概的了解.俗话说学以致用,今天就利用springAMQP来完成一个日志管理模块.大概的需求是这样的:系统中有很多地方需要记录操作日志,比如登 ...
- 中国IT职业培训市场经历的几波浪潮,未来的浪潮又是那一波?
第一波 电脑普及性培训时代 2000年至2003年左右,中国正处于PC计算机普及阶段,而IT职业教育也刚开始兴起,这一波浪潮主要以计算机办公自动化.平面设计.计算机硬件维修.为主:几家大的IT培训机构 ...
- Angular 非父子组件间的service数据通信
完成思路:以service.ts(主题subject---订阅sbuscribe模式)为数据中转中间件,通过sku.ts的数据更改监测机制,同步更改service.ts中的数据,同时buy.ts组件实 ...
- DotNetCasClient 如何获取Cas服务器返回的attributes中的数据
最近开始接触做与其它认证系统的集成,其中有个是与某学校的CAS服务器集成.cas服务器认证成功后返回的数据格式如下: 其中红色部分是我需要取出来用于识别用户身份的数据. 一开始,我根据网上的教程,引用 ...
- python爬虫小结1
先看正则化,正则化就是描述命令和字符切分.查找.筛选等功能的方便方式. http://www.cnblogs.com/fnng/archive/2013/05/20/3089816.html 一个游戏 ...
- [Maven] Missing artifact
今天从朋友那拷过来一个maven工程,eclipse中maven配置好了,maven仓库也配置完毕,但是一直报Missing artifact,然后开网执行maven update,下载完jar后,还 ...
- 《Linux系统编程手册》读书笔记——第2章基本概念
操作系统的核心--内核 内核的职责 进程调度:Linux属于抢占式多任务操作系统,多个进程可同时驻留于内存,且每个进程都能获得对CPU的使用权.哪些进程获得对CPU的使用,以及每个进程能使用多长时间 ...
- AO之Addins开发[杂谈1] Toolbar中添加一条分割线
在XML代码中,给Item添加separator属性,需要从哪里打分割线,就将其设置为true即可.如下图所示: 如紫色框住的灰色竖线所示. 默认separator属性是false的,这个小东西极其隐 ...