启动类是QuorumPeerMain类。在main方法中执行initializeAndRun方法。
在initializeAndRun方法中主要做了三件事:
加载解析配置文件 将配置文件加载到Properties cfg对象中,解析cfg对象。zookeeper所有配置信息封装到一个QuorumPeerConfig对象中
启动定时清除任务 PurgeTask继承TimeTask,定时执行run方法中的purge方法 purge方法主要清除旧的快照和日志文件
启动zk zookeeper启动方式分为两种:单机启动和集群启动
单机: 单机启动的源码 main方法调用initializeAndRun方法,initializeAndRun首先加载配置文件,然后执行runFromConfig(config)方法
启动过程首先开启一下metrics监控,然后启动admin server,然后启动zk server ServerCnxnFactory中startup方法调用NettyServerCnxnFactory实现类启动方法
集群: 在runFromConfig执行过程中主要是QuorumPeer对象属性的赋值并执行start方法,通过查看QuorumPeer类的源码,发现QuorumPeer继承了ZooKeeperThread,而ZooKeeperThread继承了Thread,通过start方法启动了QuorumPeer线程,线程运行执行线程的run方法
核心逻辑在while循环中,判断节点的状态,分为 LOOKING 、 OBSERVING 、 FOLLOWING 、 LEADING ,当某个QuorumPeerq刚启动时,状态为 LOOKING ,启动线程将zk节点启动,然后进行leader选举,这是zookeeper的选举算法的核心,leader的选举在 org.apache.zookeeper.server.quorum.FastLeaderElection的lookForLeader方法中
leader选举 此处两个变量,一个recvset,用来保存当前server的接受其他server的本轮投票信息,key为当前server的id,也即是我们在配置文件中配置的myid,而另外一个变量outofelection保存选举结束以后法定的server的投票信息,这里的法定指的是FOLLOWING和LEADING状态的server,不包活OBSERVING状态的server。
更新逻辑时钟,此处逻辑时钟是为了在选举leader时比较其他选票中的server中的epoch和本地谁最新,然后将自己的选票proposal发送给其他所有server。
sendNotifications() 此方法遍历所有投票参与者集合,将选票信息构造成一个ToSend对象,分别发送消息放置到队列sendqueue中。同理集群中每一个server节点都会将自己的选票发送给其他server,那么既然有发送选票,肯定存在接受选票信息,并选出leader,接下来我们就来看看每一个server如何接受选票并处理的。
首先我们应该从队列出取出选票信息 选出的选票信息封装在一个 Notification 对象中,如果取出的选票为null,我们通过QuorumCnxManager检查发送队列中是否投递过选票,如果投递过说明连接并没有断开,则重新发送选票到其他sever,否则,说明连接断开,重连所有server即可。那么连接没有断开,为什么会收不到选票信息呢,有可能是选票超时时限导致没有收到选票,所有将选票时限延长了一倍。
如果选出的选票Notification不为null,校验投票server和选举leader是否合法,然后根据选票状态执行不同分支,选举过程走LOOKING分支,接下来比较选票epoch和当前逻辑时钟
如果选票epoch>逻辑时钟,说明选票是最新的,自己的选票这一轮已经过时,应该更新当前自己server的逻辑时钟,并清空当前收到的其他server的选票,然后比较自己和选票中谁更适合做leader,发送新的投票给其他所有server。
如果选票epoch<逻辑时钟,zk放弃此次选票,不做任何处理。
如果选票epoch=逻辑时钟,仍然是比较选票和当前自己server谁更适合当leader,并重新更新选票,发送给其他所有的server
接下来将收到的选票放入recvset的map中保存
接下来是判断本轮选举是否结束,如果超过半数的,则leader预选举结束。如果在选票找不到任何一个server比当前server更适合做leader,则更新更新server状态,清空recvqueue队列,确定最终选票并返回,否则将更适合做leader的Notification放回队列开始新一轮的选举。
更新状态后,若选票中的服务器状态为FOLLOWING或者LEADING时,其大致步骤会再次判断选举epoch是否等于逻辑时钟.如果相等,再次盘检查选中的leader过半