集群概况zookeper在生产环境中通常部署在一个集群中,以保证高可用。下面是zookeeper官网给出的集群部署结构图:从上图可以看出,zookeeper服务器的每个节点都与master节点保持通信,每个节点存储数据和日志备份。只有当大多数节点可用时,集群才可用。本文主要基于zookeeper3.8.0的讲解。主要通过源码的维度来分析zookeeper的选举过程。zookeeper源码编译可以参考:编译运行Zookeeper源码集群节点状态集群节点状态定义在QuorumPeer#ServerState枚举中,主要包括LOOKING、FOLLOWING、LEADING、OBSERVING四种状态,下面是定义的代码和描述publicenumServerState{//寻找领导者状态。服务器处于该状态时,会认为当前集群中没有leader,因此需要进入leader选举状态。LOOKING,//粉丝状态。表示当前服务器角色是follower。FOLLOWING,//领导者状态。指示当前服务器角色是领导者。LEADING,//观察者状态。表明当前服务器角色是观察者。OBSERVING}Leader选举过程启动并初始化QuorumPeerMain是zookeeper的启动类,通过main方法启动//不显示非核心代码publicstaticvoidmain(String[]args){QuorumPeerMainmain=newQuorumPeerMain();main.initializeAndRun(args);}protectedvoidinitializeAndRun(String[]args)throwsConfigException,IOException,AdminServerException{//集群模式启动if(args.length==1&&config.isDistributed()){runFromConfig(config);}else{}}publicvoidrunFromConfig(QuorumPeerConfigconfig)throwsIOException,AdminServerException{//quorumPeer启动quorumPeer.start();}QuorumPeer是一个线程实例类。调用start方法时,会调用QuorumPeer#run()方法判断集群状态,最后进入一系列是否进行选举或同步集群节点数据信息。以下是核心代码:@Overridepublicvoidrun(){try{while(running){switch(getPeerState()){caseLOOKING://给自己投票setCurrentVote(makeLEStrategy().lookForLeader());break;caseOBSERVING:setObserver(makeObserver(logFactory));observer.observeLeader();break;caseFOLLOWING:setFollower(makeFollower(logFactory));follower.followLeader();break;caseLEADING:setLeader(makeLeader(logFactory));leader.lead();setLeader(null);break;}}}finally{}}进行选举FastLeaderElection是选举的核心类,在这个类中有投票和投票的过程publicVotelookForLeader()throwsInterruptedException{//创建当前electionPeriodicballotboxMaprecvset=newHashMap();//创建一个投票箱这个投票箱不同于recvset。//存储投票Mapoutofelection=newHashMap();intnotTimeout=minNotificationInterval;synchronized(this){//如果已经有Leader则增加本地选举周期logicalclock.incrementAndGet()当前集群;//自己投票为false,则执行一次选举recvset.clear();//totalOrderPredicate投票PKif(totalOrderPredicate(n.leader,n.zxid,n.peerEpoch,getInitId(),getInitLastLoggedZxid(),getPeerEpoch())){updateProposal(n.leader,n.zxid,n.peerEpoch);}else{updateProposal(getInitId(),getInitLastLoggedZxid(),getPeerEpoch());}sendNotifications();}elseif(totalOrderPredicate(n.leader,n.zxid,n.peerEpoch,proposedLeader,proposedZxid,proposedEpoch)){updateProposal(n.leader,n.zxid,n.peerEpoch);sendNotifications();}//监听通信层收到投票Notificationn=recvqueue.poll(notTimeout,TimeUnit.MILLISECONDS);//放入投票箱recvset.put(n.sid,newVote(n.leader,n.zxid,n.electionEpoch,n.peerEpoch));//一半以上的逻辑voteSet=getVoteTracker(recvset,newVote(proposedLeader,proposedZxid,logicalclock.get(),proposedEpoch));}}totalOrderPredicate主要是投票PK的逻辑,我们再看一下代码:protectedbooleantotalOrderPredicate(longnewId,longnewZxid,longnewEpoch,longcurId,longcurZxid,longcurEpoch){if(self.getQuorumVerifier().getWeight(newId)==0){returnfalse;}返回((newEpoch>curEpoch)||((newEpoch==curEpoch)&&((newZxid>curZxid)||((newZxid==curZxid)&&(newId>curId)))));}选举过程看起来是这样的,其实官方也给了个说明:先比较个数elections,选举次数越多,最新的一个session,winner会比较zxid,也就是看谁的数据最新,最新的win最后会比较serverid,在配置文件中指定,和ID较大的节点将获胜。选举完成后,sendNotifications();通知其他节点。流程总结前面我粗略的讲解了zookeeper从启动流程到选举,选举结果同步,如何投票选举结果确认的过程,但是zookeeper作为一个高性能,高可靠的分布式协调中间件,也是许多设计细节非常详细。出色的。投票过程通常情况下,投票过程中zxid越大,成为leader的可能性越大。主要是zxid越大,节点的数据越多,这样可以减少数据同步过程中节点事务的撤销和日志文件的同步。比较流程以提高性能。下面是5个zookeeper节点选举的过程。注:(sid,zxid),当前场景为server1,server2故障,server3的zxid=9,server4和server5的zxid为8。经过两轮选举,最终选出server3作为leader节点。多层网络架构在之前的分析过程中,我省略了Zookeeper节点间通信的NIO操作。这部分,简单来说,Zookeeper把他们分为传输层和业务层。网络层的数据包由SendWorker和RecvWorker处理,业务层的数据由WorkerSender和WorkerReceiver处理。这将涉及到多线程操作。Zookeeper在源码中也提供了大量的日志信息,这对于初学者来说是有难度的。对此,可以参考如下Zookeeper选举源码流程流程图,辅助分析。Leader选举源码流程结合上面的梳理,我对zookeeper启动和选举的流程做了一个比较详细的梳理。大家可以结合zookeeper源码来理解。参考文档apachezookeeper官网Zookeeper的leader选举机制解析了解Zookeeper的Leader选举过程