1. QuorumPeerMain运行

1)判断是采用单实例模式还是多实例模式启动QuorumPeerMain

2)在多实例模式下,加载启动参数中指定的配置文件

3)启动QuorumPeer

public class QuorumPeerMain {
... protected QuorumPeer quorumPeer; public static void main(String[] args) {
QuorumPeerMain main = new QuorumPeerMain();
try {
main.initializeAndRun(args);
}
...
} protected void initializeAndRun(String[] args) throws ConfigException, IOException {
QuorumPeerConfig config = new QuorumPeerConfig();
if (args.length == 1) {
config.parse(args[0]);
}
// 启动data目录下的定时清理任务
...
// 启动参数中指定配置文件,且配置文件中指定为多实例
if (args.length == 1 && config.servers.size() > 0) {
runFromConfig(config); //
} else {
// 单实例模式启动
...
}
} public void runFromConfig(QuorumPeerConfig config) throws IOException {
...
try {
// 客户端连接工厂,默认NIOServerCnxnFactory
ServerCnxnFactory cnxnFactory =
ServerCnxnFactory.createFactory();
cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns());
quorumPeer = new QuorumPeer(config.getServers(),
new File(config.getDataDir()),
new File(config.getDataLogDir()),
config.getElectionAlg(), // 选举算法,默认FastLeaderElection(3)
config.getServerId(),
config.getTickTime(),
config.getInitLimit(),
config.getSyncLimit(),
config.getQuorumListenOnAllIPs(),
cnxnFactory,
config.getQuorumVerifier());
...
quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
...
quorumPeer.start(); // 启动当前实例
quorumPeer.join(); // 主线程等待quorumPeer线程执行
} catch (InterruptedException e) {
...
}
}
}

2. QuorumPeer启动

1)监听客户端连接

2)确认选举算法,监听选举端口

3)启动服务(run),开始选举

4 ) 成为Leader / Follower / Observer(后续介绍)

public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider {
...
ServerCnxnFactory cnxnFactory; // NIOServerCnxnFactory
...
@Override
public synchronized void start() {
loadDataBase(); // 加载currentEpoch和acceptedEpoch
cnxnFactory.start(); // 监听客户端连接,NIOServerCnxnFactory.start
startLeaderElection(); // 确认选举算法,监听选举端口
super.start(); // 启动服务(run),开始选举
}
...
synchronized public void startLeaderElection() {
try {
// 当前投票投给自己
currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
} catch(IOException e) {
...
}
...
// 确认选举算法,监听选举端口
this.electionAlg = createElectionAlgorithm(electionType);
}
...
protected Election createElectionAlgorithm(int electionAlgorithm){
Election le=null;
switch (electionAlgorithm) {
...
case 3:
qcm = createCnxnManager(); // QuorumCnxManager负责收发投票
QuorumCnxManager.Listener listener = qcm.listener;
if(listener != null){
listener.start(); // 启动ServerSocket监听选举端口
le = new FastLeaderElection(this, qcm);
}
...
break;
...
}
return le;
}
...
@Override
public void run() {
...
try {
while (running) {
switch (getPeerState()) {
case LOOKING:
if (Boolean.getBoolean("readonlymode.enabled")) {
// 启动ReadOnlyZooKeeperServer处理只读请求
...
} else {
try {
setBCVote(null);
// 开始选举,FastLeaderElection.lookForLeader
setCurrentVote(makeLEStrategy().lookForLeader());
}
...
}
break;
case OBSERVING:
try {
setObserver(makeObserver(logFactory)); // Observer
observer.observeLeader();
}
...
break;
case FOLLOWING:
try {
setFollower(makeFollower(logFactory)); // Follower
follower.followLeader();
}
...
break;
case LEADING:
try {
setLeader(makeLeader(logFactory)); // Leader
leader.lead();
setLeader(null);
}
...
break;
}
}
} finally {
...
}
}
}

3. 快速选举算法

1)广播推荐的Leader为自己,并接收其它ZK服务器的投票

2)选举进行中

  1' 若对方推荐的Leader > 自己推荐Leader,跟随对方投票并广播

  2' 更新票箱,判断对方投票是否过半,是则返回最终投票,否则选举继续

3)选举已经结束

  更新票箱,判断对方投票是否过半且为Leader,是则返回最终投票,否则选举继续

public class FastLeaderElection implements Election {
...
public FastLeaderElection(QuorumPeer self, QuorumCnxManager manager){
...
starter(self, manager);
}
private void starter(QuorumPeer self, QuorumCnxManager manager) {
...
sendqueue = new LinkedBlockingQueue<ToSend>(); // 待发送投票队列
recvqueue = new LinkedBlockingQueue<Notification>(); // 待接收投票队列
// 通过QuorumCnxManager往sendqueue添加投票,从recvqueue获取投票
this.messenger = new Messenger(manager);
}
...
public Vote lookForLeader() throws InterruptedException {
...
try {
HashMap<Long, Vote> recvset = new HashMap<Long, Vote>(); // 投票箱
// 如当前ZK服务器新加入到集群时,选举已经结束且当前选举轮数落后
HashMap<Long, Vote> outofelection = new HashMap<Long, Vote>();
...
synchronized(this){
logicalclock++; // 选举轮数 + 1
// 初始推荐Leader为自己,即初始投票投给自己
updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
}
...
sendNotifications(); // 广播自己的投票
// 循环直至选举出Leader
while ((self.getPeerState() == ServerState.LOOKING) && (!stop)){
// 获取一个其它ZK服务器的投票(超时时间为200毫秒)
Notification n = recvqueue.poll(notTimeout, TimeUnit.MILLISECONDS);
if(n == null){
// 重新广播自己的投票,增加超时时间
...
}
else if(self.getVotingView().containsKey(n.sid)) {
switch (n.state) {
case LOOKING: // 选举进行中
if (n.electionEpoch > logicalclock) { // 当前选举轮数落后
logicalclock = n.electionEpoch; // 更新选举轮数
recvset.clear(); // 清空票箱
// 对方投票 > 当前投票(对方推荐的LeaderID > 自己推荐的LeaderID)
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;
} else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, proposedLeader, proposedZxid, proposedEpoch)) {
updateProposal(n.leader, n.zxid, n.peerEpoch); // 跟随对方投票
sendNotifications(); // 重新广播自己的投票
}
...
// 更新票箱
recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));
// 票数过半
if (termPredicate(recvset, new Vote(proposedLeader, proposedZxid, logicalclock, proposedEpoch))) {
// 等待200ms接收投票
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) { // 200ms内未接收到新的投票
// 服务器状态由LOOKING转为LEADING/FOLLOWING/OBSERVING
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:
// 如当前ZK服务器新加入到集群时,选举已经结束
if(n.electionEpoch == logicalclock) { // ??为何选举轮数落后则使用outofelection
// 更新recvset票箱,并查找当前集群Leader
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;
}
}
// 更新outofelection票箱,并查找当前集群Leader
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()) ? erverState.LEADING: learningState());
}
Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch);
leaveInstance(endVote);
return endVote;
}
break;
...
}
}
...
}
return null;
}
...
}
}

Zookeeper启动和集群选举的更多相关文章

  1. zookeeper全局数据一致性及其典型应用(发布订阅、命名服务、帮助其他集群选举)

    ZooKeeper全局数据一致性: 全局数据一致:集群中每个服务器保存一份相同的数据副本,client 无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征. 那么zookeeper集群是怎样 ...

  2. Zookeeper(4)---ZK集群部署和选举

    一.集群部署 1.准备三台机器,安装好ZK.强烈建议奇数台机器,因为zookeeper 通过判断大多数节点的存活来判断整个服务是否可用.3个节点,挂掉了2个表示整个集群挂掉,而用偶数4个,挂掉了2个也 ...

  3. 备忘zookeeper(单机+伪集群+集群)

    #下载: #单机模式 解压到合适目录. 进入zookeeper目录下的conf子目录, 复制zoo_sample.cfg-->zoo.cfg(如果没有data和logs就新建):tickTime ...

  4. HyperLedger Fabric基于zookeeper和kafka集群配置解析

    简述 在搭建HyperLedger Fabric环境的过程中,我们会用到一个configtx.yaml文件(可参考Hyperledger Fabric 1.0 从零开始(八)--Fabric多节点集群 ...

  5. zookeeper及kafka集群搭建

    zookeeper及kafka集群搭建 1.有关zookeeper的介绍可参考:http://www.cnblogs.com/wuxl360/p/5817471.html 2.zookeeper安装 ...

  6. Zookeeper简介与集群搭建【转】

    Zookeeper简介 Zookeeper是一个高效的分布式协调服务,可以提供配置信息管理.命名.分布式同步.集群管理.数据库切换等服务.它不适合用来存储大量信息,可以用来存储一些配置.发布与订阅等少 ...

  7. Zookeeper单机伪集群

    Zookeeper单机伪集群 1.配置 zookeeper下载地址:http://apache.mirrors.lucidnetworks.net/zookeeper/ 可以选择需要的版本,我下载的是 ...

  8. zookeeper 安装以及集群搭建

    安装环境: jdk1.7 zookeeper-3.4.10.tar.gz VM虚拟机redhat6.5-x64:192.168.1.200  192.168.1.201  192.168.1.202 ...

  9. zookeeper 高可用集群搭建

    前言 记录Zookeeper集群搭建的过程! 什么是 Zookeeper ? ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hado ...

随机推荐

  1. IE6透明PNG解决方案

    IE6不支持PNG-24图片一直困扰很多人,但是可以通过IE的独有的滤镜来解决,解决的方案很多,比如:将滤镜写在CSS里,还可以写成单独的 Javascript文件,本来认为推荐两种做法:第一种,将所 ...

  2. Mysql储存过程8:repeat循环

    语法: repeat SQL until 条件 end repeat; 就是相当于其他语言中的: do{ # }while(); mysql> create procedure p1p() -& ...

  3. 安装在Ubuntu上的Python虚拟环境

    安装指南是在 Ubuntu 下面操作的.不同的 Linux 版本,安装指令不同.所以,该指南的某些指令对于像 CentOS 等非 Ubuntu 系统不适用. 为什么需要使用虚拟环境? 虚拟环境是一个将 ...

  4. .htaccess技巧: URL重写(Rewrite)与重定向(Redirect)

    URL重定向是.htaccess的重头戏,它可以将长地址转为短地址.将动态地址转为静态地址.重定向丢失的页面.防止盗链.实现自动语言转换等.笔者觉得难点是在正则表达式的运用和理解上. 实现所有这些神奇 ...

  5. DBCP object created 日期 by the following code was never closed:

    1.分析 看到标题 DBCP 首先想到的肯定是 数据库连接池哪方面有问题,那么先别着急去解决,不要一股脑就钻进逻辑代码中,然后启用调试就开始一步一步 的分析.我们首先要做的就是想,想想数据库连接池,在 ...

  6. xcode上真机调试iphone4s出现“There was an internal API error.”解决方案

    xcode7更新之后使用真机调试,在IOS8的一台Iphone5手机上面没什么问题,IOS8的一台iphone6也没问题.但是在IOS6的一台Iphone4s和 IOS7的ipad air2上面在最后 ...

  7. hive的窗口函数ntile、row_number、rank

    一.ntile 序列函数不支持window子句 数据准备: cookie1,--, cookie1,--, cookie1,--, cookie1,--, cookie1,--, cookie1,-- ...

  8. 简易代理服务器之python实现

    代理服务器是在client和server之间的一个服务器,一般起到缓存的作用,所以也叫缓存服务器.比如: A ----(HTTP)----> B ----(HTTP)----> C 其中A ...

  9. angular.js 验证码注册登录

    css部分 header{ height: 50px; line-height: 50px; display: flex; } .callback{ text-align: left; display ...

  10. Hadoop案例(二)压缩解压缩

    压缩/解压缩案例 一. 对数据流的压缩和解压缩 CompressionCodec有两个方法可以用于轻松地压缩或解压缩数据.要想对正在被写入一个输出流的数据进行压缩,我们可以使用createOutput ...