Zookeeper启动和集群选举
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启动和集群选举的更多相关文章
- zookeeper全局数据一致性及其典型应用(发布订阅、命名服务、帮助其他集群选举)
		ZooKeeper全局数据一致性: 全局数据一致:集群中每个服务器保存一份相同的数据副本,client 无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征. 那么zookeeper集群是怎样 ... 
- Zookeeper(4)---ZK集群部署和选举
		一.集群部署 1.准备三台机器,安装好ZK.强烈建议奇数台机器,因为zookeeper 通过判断大多数节点的存活来判断整个服务是否可用.3个节点,挂掉了2个表示整个集群挂掉,而用偶数4个,挂掉了2个也 ... 
- 备忘zookeeper(单机+伪集群+集群)
		#下载: #单机模式 解压到合适目录. 进入zookeeper目录下的conf子目录, 复制zoo_sample.cfg-->zoo.cfg(如果没有data和logs就新建):tickTime ... 
- HyperLedger Fabric基于zookeeper和kafka集群配置解析
		简述 在搭建HyperLedger Fabric环境的过程中,我们会用到一个configtx.yaml文件(可参考Hyperledger Fabric 1.0 从零开始(八)--Fabric多节点集群 ... 
- zookeeper及kafka集群搭建
		zookeeper及kafka集群搭建 1.有关zookeeper的介绍可参考:http://www.cnblogs.com/wuxl360/p/5817471.html 2.zookeeper安装 ... 
- Zookeeper简介与集群搭建【转】
		Zookeeper简介 Zookeeper是一个高效的分布式协调服务,可以提供配置信息管理.命名.分布式同步.集群管理.数据库切换等服务.它不适合用来存储大量信息,可以用来存储一些配置.发布与订阅等少 ... 
- Zookeeper单机伪集群
		Zookeeper单机伪集群 1.配置 zookeeper下载地址:http://apache.mirrors.lucidnetworks.net/zookeeper/ 可以选择需要的版本,我下载的是 ... 
- 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 ... 
- zookeeper 高可用集群搭建
		前言 记录Zookeeper集群搭建的过程! 什么是 Zookeeper ? ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hado ... 
随机推荐
- python基础===对字符串进行左右中对齐
			例如,有一个字典如下: >>> dic = { "name": "botoo", "url": "http:// ... 
- 前端nginx时,让后端tomcat记录真实IP【转】
			对于nginx+tomcat这种架构,如果后端tomcat配置保持默认,那么tomcat的访问日志里,记录的就是前端nginx的IP地址,而不是真实的访问IP.因此,需要对nginx.tomcat做如 ... 
- [转载]Selenium実行中にJavaScriptのコードを実行する
			Selenium実行中にJavaScriptのコードを実行する JavaScriptで画面の値を取得/設定するコードをメモ. WebDriverEx.cs // JavaScriptを実行(戻り値なし ... 
- 010 JVM类加载
			转自http://www.importnew.com/23742.html 前言 我们知道我们写的程序经过编译后成为了.class文件,.class文件中描述了类的各种信息,最终都需要加载到虚拟机之后 ... 
- Tomcat: Connector中HTTP与AJP差别与整合
			apache tomcat 整合(ajp proxy, http proxy) 1.软件: apache: httpd-2.2.17-win32-x86-openssl-0.9.8o.msi tomc ... 
- Oracle中的dual
			简介,摘自百度百科: Oracle提供的最小的表,不论进行何种操作(不要删除记录),它都只有一条记录——'X'. 例如:执行select * from dual,里面只有一条记录:执行insert i ... 
- CentOS 7 中 Docker 的安装
			CentOS 7 中 Docker 的安装 Docker 软件包已经包括在默认的 CentOS-Extras 软件源里.因此想要安装 docker,只需要运行下面的 yum 命令: [root@loc ... 
- fastJson去掉指定字段
			public static String filterFieldsJson(Object src, Class<?> clazz, String... args) { SimpleProp ... 
- Java Tuple使用实例(转)
			转自链接:http://www.cnblogs.com/davidwang456/p/4514659.html 一.为什么使用元组tuple? 元组和列表list一样,都可能用于数据存储,包含多个数据 ... 
- python tornado 中使用 flash消息闪现
			1.html 中引入文件 {% block head %} <link href="/static/common/sweetalert/sweetalert.css" rel ... 
