服务器状态

在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. Java面向对象的特征

    面向对象的特征 封装.继承.多态.(有人问第四个特征,再加抽象) 封装 体现形式(2种) 函数---提高代码的复用性 属性的私有化---将属性设置为私有的,通过提供对外的访问方法来间接操作对应属性,可 ...

  2. String类为什么要用final修饰(面试回答)

    String是所有语言中最常用的一个类.我们知道在Java中,String是不可变的.final的.Java在运行时也保存了一个字符串池(String pool),这使得String成为了一个特别的类 ...

  3. 《Netty5.0架构剖析和源码解读》【PDF】下载

    <Netty5.0架构剖析和源码解读>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062545 内容简介 Netty 是个异步的 ...

  4. 关于Object类下所有方法的简单解析

    类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直接来看jdk的源码,开始我们的分析之路 ...

  5. iOS 图片的拉伸,取固定区域显示

    1.图片拉伸合适的尺寸 以及清晰度 UIButton * but =[[UIButton alloc]initWithFrame:CGRectMake(, , , )]; //拉伸 /*UIImage ...

  6. Android 环境搭建、基础窗口window/Mac

    1.五步搞定Android开发环境部署--非常详细的Android开发环境搭建教程 2.Android开发学习之路--MAC下Android Studio开发环境搭建 4.Android常用开发工具以 ...

  7. 工厂方法模式(Method Factory),理解多态应用的好例子.

    工厂方法模式又称为多态性工厂,个人认为多态性工厂更能准确的表达这个模式的用处.与简单工厂(静态工厂)相比较,这里的多态性是指抽象出一个工厂基类,将因为一个产品有N种不同的是现这种变化封装起来,将具体的 ...

  8. class_copyIvarList方法获取实例变量问题引发的思考

    在runtime.h中,你可以通过其中的一个方法来获取实例变量,那就是class_copyIvarList方法,具体的实现如下: - (NSArray *)ivarArray:(Class)cls { ...

  9. 【quickhybrid】iOS端的项目实现

    前言 18年元旦三天内和朋友突击了下,勉强是将雏形做出来了,后续的API慢慢完善.(当然了,主力还是那个朋友,本人只是初涉iOS,勉强能看懂,修修改改而已) 大致内容如下: JSBridge核心交互部 ...

  10. TreeMap源码

    一.TreeMap简介 TreeMap是基于红黑树的java版实现,作者Josh Bloch and Doug Lea(这二人在java发展的早期做了重大贡献,比如集合框架JDK1.2.并发包JDK1 ...