当前位置: 首页 > 科技观察

zk集群运行过程中,服务器选举源码分析

时间:2023-03-15 11:16:37 科技观察

在zk服务器集群启动过程中,不仅会通过QuorumPeerMain创建ZooKeeperServer对象,还会生成QuorumPeer对象,代表一个ZooKeeper集群中的机器。在整个机器运行期间,负责维护机器的运行状态,同时根据情况发起Leader选举。下图是《从PAXOS到ZOOKEEPER分布式一致性原理与实践》的服务器启动流程。QuorumPeer是一个独立的线程,维护着zk机器的状态。@Overridepublicsynchronizedvoidstart(){loadDataBase();cnxnFactory.start();startLeaderElection();super.start();}这次主要介绍选举相关的内容。至于其他的操作,可以看其他的博客。以下文字均来源于startLeaderElection。基本概念:SID:服务器ID,用于标识ZooKeeper集群中的机器,每台机器不能重复,myid的值始终为ZXID:事务IDVote:投票,具体数据结构遵循Quorum:过半的机器。Electionround:Logicalclock,zkserverLeaderelectionroundservertype:z??k中引入了Leader,Follwer,Observer三种角色。zk集群中的所有机器通过Leader选举过程选出一台名为Leader的机器,Leader服务器为客户端提供读写服务。Follower和Observer都可以提供读服务。最大的区别是Observer机器不参与Leader选举过程,也不参与一半以上写操作的写成功策略。所以Observer的意义在于在不影响写性能的情况下提高集群的读性能。服务器状态:+LOOKING:Leader选举阶段+FOLLOWING:Follower服务器和Leader处于同步状态+LEADING:Leader服务器处于主进程的领导状态。+OBSERVING:观察者状态,表示当前服务器是观察者。不参与投票的目的是选择一个合适的Leader机器。Leader机器决定事务性的Proposal处理流程,实现类似的两阶段提交协议(具体为ZAB协议)QuorumPeer维护集群机器的状态QuorumPeer负责持续检测当前zk机器的状态,并执行相应的逻辑。简单的说就是根据服务的不同状态执行不同的逻辑。删除了一部分分发后,代码如下:@Overridepublicvoidrun(){setName("QuorumPeer"+"[myid="+getId()+"]"+cnxnFactory.getLocalAddress());try{while(running){switch(getPeerState()){caseLOOKING:LOG.info("LOOKING");try{setBCVote(null);setCurrentVote(makeLEStrategy().lookForLeader());}catch(Exceptione){LOG.warn("Unexpectedexception",e);setPeerState(ServerState.LOOKING);}break;caseOBSERVING:try{LOG.info("OBSERVING");setObserver(makeObserver(logFactory));observer.observeLeader();}catch(Exceptione){LOG.warn("Unexpectedexception",e);}finally{observer.shutdown();setObserver(null);setPeerState(ServerState.LOOKING);}break;caseFOLLOWING:try{LOG.info("FOLLOWING");setFollower(makeFollower(logFactory));follower.followLeader();}catch(Exceptione){LOG.warn("Unexpectedexception",e);}finally{follower.shutdown();setFollower(null);setPeerState(ServerState.LOOKING);}break;caseLEADING:LOG.info("领导G");try{setLeader(makeLeader(logFactory));leader.lead();setLeader(null);}catch(Exceptione){LOG.warn("Unexpectedexception",e);}finally{if(leader!=null){leader.shutdown("Forcingshutdown");setLeader(null);}setPeerState(ServerState.LOOKING);}break;}}}finally{LOG.warn("QuorumPeermainthreadexited");}}当机器处于LOOKINGstate,QuorumPeer会进行选举,但是具体的逻辑不由QuorumPeer负责。整个投票过程是独立的。从逻辑执行的角度来看,整个过程设计成两个主要环节:与其他zk集群的通信过程实现具体的选举算法,QuorumPeer中默认使用的选举算法是FastLeaderElection,后面的分析也是基于FastLeaderElection选举流程整体结构在集群启动过程中,QuorumPeer会根据配置执行不同的选举策略。(this);QuorumCnxManager.Listenerlistener=qcm.listener;if(listener!=null){listener.start();le=newFastLeaderElection(this,qcm);}else{LOG.error("Nulllistenerwheninitializingcnxmanager");}break;default:assertfalse;}returnle;}如果ClientCnxn是zk客户端中处理IO请求的manager,QuorumCnxManager是zk集群间选举过程中负责网络IO的manager。当每个服务器启动时,将启动一个QuorumCnxManager。用于维护服务器之间的网络通信。对于每台zk机器,都需要建立一个TCP端口监听,交给QuorumCnxManager中的Listener处理,使用Socket的阻塞IO(默认监听端口为3888,在config文件中设置)。在两两相互连接的过程中,为了避免两台机器之间重复建立TCP连接,zk制定了连接规则:只有拥有SID的服务器才允许主动与其他服务器建立连接。实现方法也比较简单。在receiveConnection中,服务器会比较与其建立连接的服务器的SID来决定是否接受请求。如果自身的SID较大,则会断开连接,然后主动与远程服务器建立连接。这个逻辑是由Listener完成的,Listener独立线程,receivedConnection,连接建立后示意图:QuorumCnxManager是连接的管家,具体的TCP连接交给Listener,但是用于投票的管理,内部维护了一系列Queue:recvQueue:消息接收队列,用于存储从其他服务器接收到的消息,一个单独的队列分组队列(在quorumCnxManager中,zk集群中的每台机器根据SID单独分组,形成一个队列集):queueSendMap:消息发送队列,用于保存待发送的消息。新的ConcurrentHashMapsenderWordorMap:发送者的集合。每个SendWord消息发送者对应一个远程zk服务器,负责消息的分发。lastMessageSent:最近发送的消息根据SID分组。基本通信过程如下:以上内容主要是建立各个zk服务器之间的连接通信过程。具体的选举策略由zk抽象出来。主要分析的是FastLeaderElection方法(选举算法的核心部分):publicinterfaceElection{publicVotelookForLeader()throwsInterruptedException;publicvoidshutdown();}FastLeaderElection选举算法上面提到,当QuorumPeer检测到当前服务器状态为LOOKING时,新一轮将通过setCurrentVote(makeLEStrategy().lookForLeader());进行选举。即初始选择的FastLeaderElection的lookForLeader,实现方法也很简单。主要逻辑在FastLeaderElection.lookForLeader中实现:先说明基本流程:QuorumPeer会轮询查看当前服务器状态,如果发现State为LOOKING,则调用Election的lookForLeader开始新一轮选举FastLeaderElection会先设置logicallock++,表示新一轮的选举已经开始构建初始选票,Vote的内容是选择自己,然后通知zk集群中的其他机器FastLeaderElection会一直轮询状态。只要处于LOOKING状态,就会从recvqueue中获取其他服务器同步的选票信息。为便于说明,记为n。根据n的选票信息状态,做相关操作LOOKING:都在NoLeaderFOLLOWING,LEADING:表示集群中已经有Leader,更新自己的状态,结束本轮投票。OBSERVING:此票无用,直接丢弃(OBSERVER不参与投票)。根据上面的流程,我们可以大致解释一下FasterLeaderElection的策略,以确定更好的选票:然后更新选票如果选举如果回合一致,则比较两者的ZXID。在ZAB协议中,ZXID越大,保留的信息就越多。因此,如果ZXID大于自己的,则更新投票。如果ZXID也相同,则比较两者的SID。优先级高总结:以上是zk默认的投票流程,根据ZAB协议的两种状态来分析:初始化时,同轮投票,直到通过投票选出Leader崩溃恢复阶段:Leader服务器挂了,然后经历过程和初始化过程类似。如果选择LeaderFollower服务器挂掉,那么在选举过程中会收到其他服务器的Leader投票信息,也可以确定Leader。