微服务系列(二)(2) ZooKeeper源码分析-part-1
本节开始进行ZooKeeper的源码分析,针对Zookeeper Server的初始化过程,通讯原理及选举机制做源码层面上的介绍
作为ZooKeeper的使用者,应该知道如何部署和启动Zookeeper吧
看源码的入口就从这里作为出发点,找到zkServer.sh或者zkServer.cmd
截取zkServer.sh里的一段重要的命令
nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
"-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
以及
ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
找到了程序入口QuorumPeerMain
类
进入org.apache.zookeeper.server.quorum.QuorumPeerMain#main
具体链路就不追踪了,做了三件事:
- 加载配置文件
- 启动清除日志管理器(用于定时清除日志和快照数据)
- 启动
ZooKeeperServerMain
(单机模式下)或org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig
(非单机模式下)
继续追踪ZooKeeperServerMain
的启动过程
忽略JMX等相关内容,这里的启动同样加载了配置文件,并进入核心启动方法org.apache.zookeeper.server.ZooKeeperServerMain#runFromConfig
:
-
MetricsProvider
引导程序的初始化和启动,用于度量以及与外部系统交互,目前猜测与选举相关(官方注释:A MetricsProvider is a system which collects Metrics and publishes current values to external facilities.) -
FileTxnSnapLog
初始化,用于把文件存储的日志和快照载入到JVM并定期写入文件(它会被包裹在ZKDatabase
里,由ZKDatabase
直接与ZooKeeperServer
中的其他组件交互) -
ZooKeeperServer
初始化和启动,启动有两种模式Netty和NIO,默认采用NIO模式启动,具体的启动步骤就不介绍了,无非是各种工作线程的初始化和启动、参数配置、端口配置、请求处理器配置等 -
ContainerManager
初始化和启动,用于定期检测容器情况,确保容器正常运行
这里我们需要关注的是ZooKeeperServer
,关注它的startup()
方法
public synchronized void startup() {
if (sessionTracker == null) {
createSessionTracker();
}
startSessionTracker();
setupRequestProcessors();
registerJMX();
setState(State.RUNNING);
notifyAll();
}
这里又出现了几个新的类:
-
SessionTracker
:用于session信息的保存、创建、销毁、定时检测是否失效 -
RequestProcessor
:用于请求处理,包括sync、send、commit等命令执行的操作
差不多到这里,单机模式下的ZooKeeperServerMain
类,后面在看集群模式下的过程中会用到它的东西。
回归org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig
与单机模式不同的地方在于,它启动的是QuorumPeer
而不是ZooKeeperServer
:
QuorumPeer
初始化过程:
初始化权限相关的类QuorumAuthServer
和QuorumAuthLearner
,分别用于认证server和learner。
QuorumPeer
启动过程:
- 初始化zkDb(前面看到的包裹了
FileTxnSnapLog
的ZKDatabase
),这里与单机模式下存在不同的地方,它额外增加了epoch校验的逻辑,不允许出现zxid大于当前epoch的情况。 - 启动
ServerCnxnFactory
(同单机模式下) - 启动
AdminServer
(同单机模式下) - 启动选举
org.apache.zookeeper.server.quorum.QuorumPeer#startLeaderElection
,重点来了!
org.apache.zookeeper.server.quorum.QuorumPeer#startLeaderElection
synchronized public void startLeaderElection() {
try {
//初始化自己持有的投票信息
if (getPeerState() == ServerState.LOOKING) {
currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
}
} catch(IOException e) {
RuntimeException re = new RuntimeException(e.getMessage());
re.setStackTrace(e.getStackTrace());
throw re;
}
//初始化选举算法类
this.electionAlg = createElectionAlgorithm(electionType);
}
protected Election createElectionAlgorithm(int electionAlgorithm){
Election le=null;
//TODO: use a factory rather than a switch
switch (electionAlgorithm) {
case 1:
//AuthFastLeaderElection已废弃
le = new AuthFastLeaderElection(this);
break;
case 2:
//AuthFastLeaderElection已废弃
le = new AuthFastLeaderElection(this, true);
break;
case 3:
QuorumCnxManager qcm = createCnxnManager();
QuorumCnxManager oldQcm = qcmRef.getAndSet(qcm);
if (oldQcm != null) {
LOG.warn("Clobbering already-set QuorumCnxManager (restarting leader election?)");
oldQcm.halt();
}
QuorumCnxManager.Listener listener = qcm.listener;
if(listener != null){
listener.start();
FastLeaderElection fle = new FastLeaderElection(thi