概述
本来希望能够通过简单的图表的方式把作者的设计思想表达出来,但这种写法比较消耗时间,但由于时间的原因,这段时间会推出源码走读的文章。
所谓源码走读,就是通过阅读关键流程或核心代码来理解作者的设计思想和代码设计。最好的文档就是源代码。
本文讲述zookeeper集群单个节点的启动流程。
总体启动流程
zookeeper的总体启动流程,我前面专门有一篇文章讲述,这里再回顾一下:
## 总体流程
QuorumPeerMain.main()
-> initializeAndRun()
-> QuorumPeerConfig.parse()
-> DatadirCleanupManager.start()
-> if (args.length == 1 && config.servers.size())
-> QuorumPeerMain.runFromConfig() //集群模式
else
-> ZooKeeperServerMain.main(args) //单机模式
具体实现分析
initializeAndRun函数
在类org.apache.zookeeper.server.quorum.QuorumPeerMain中initializeAndRun作为初始化函数,主要完成以下事项:
- 创建一个QuorumPeerConfig配置类,并读取配置文件中的配置信息。
- 创建DatadirCleanupManager并启动,该类通过使用指定的“autopurge.purgeInterval”调度自动清除任务来管理快照和相应事务日志的清理。 它保留最新的’autopurge.snapRetainCount’数量的快照和相应的事务日志。
- 若是分布式模式,调用runFromConfig(config);进入分布式模型运行,若是单机,则运行 ZooKeeperServerMain.main(args);进入单机服务模式。
该函数的实现代码如下:
protected void initializeAndRun(String[] args)
throws ConfigException, IOException, AdminServerException
{
// 创建配置类,读取配置文件信息
QuorumPeerConfig config = new QuorumPeerConfig();
if (args.length == 1) {
config.parse(args[0]);
}
// Start and schedule the the purge task
// 启动并调度日志和快照数据清除服务
DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config
.getDataDir(), config.getDataLogDir(), config
.getSnapRetainCount(), config.getPurgeInterval());
purgeMgr.start();
if (args.length == 1 && config.isDistributed()) {
// 若配置成分布式集群模式,运行这里
runFromConfig(config);
} else {
// 单机模式,运行这里
LOG.warn("Either no config or no quorum defined in config, running "
+ " in standalone mode");
// there is only server in the quorum -- run as standalone
ZooKeeperServerMain.main(args);
}
}
runFromConfig函数
该函数是zookeeper进入分布式集群模式的入口函数,该函数主要流程如下:
- 通过工厂方法创建ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();服务器类,该类用来接收客户端的连接请求,后面还会详细分析。创建完成后,还会调用该类的configure函数对该服务绑定的地址和端口进行设置,服务的地址和端口可以在zoo.cfg中配置参数:clientPortAddress和clientPort。
- 若配置了secureClientPortAddress参数,还需要再开启一个NIO后台服务,secureCnxnFactory = ServerCnxnFactory.createFactory();
- 创建一个QuorumPeer类的对象:quorumPeer = getQuorumPeer();并对该对象进行参数设置和初始化。
- 进入QuorumPeer类的初始化设置,主要是读取配置文件,根据配置文件设置配置项,包括:
- 交易日志和快照处理类:FileTxnSnapLog logFactory,初始化成:new FileTxnSnapLog(…)
- 设置选举的类型:setElectionType(config.getElectionAlg())
- 设置自己的id:setMyid(config.getServerId())
- 设置zookeeper的数据库管理类:setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
- 设置可使用的最大线程池:setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize)
- 调用initialize函数进行初始化,该函数主要是对认证对象进行创建。
- 还有一些session相关的设置
- 最后调用quorumPeer.start();启动后台服务。
QuorumPeer.start()函数
该函数正式启动后台的服务,我们来看一下该函数的具体步骤:
- 调用loadDataBase加载数据到zookeeper的树形数据库中。
- 调用startServerCnxnFactory函数启动后台服务,该服务用来接收和处理客户端发送的请求。
- 调用adminServer.start();启动AdminServer服务,该服务默认是JettyAdminServer,通过该服务可以通过http的方式来向zookeeper发送命令。
- 调用startLeaderElection函数,开启zookeeper的选主流程。
- 启动QuorumPeer线程。
start()函数的代码如下:
@Override
public synchronized void start() {
if (!getView().containsKey(myid)) {
throw new RuntimeException("My id " + myid + " not in the peer list");
}
// 加载数据库的数据
loadDataBase();
// 开启后台NIOServerCnxnFactory服务,接收并处理客户端的命令
startServerCnxnFactory();
try {
// 启动管理服务器
adminServer.start();
} catch (AdminServerException e) {
LOG.warn("Problem starting AdminServer", e);
System.out.println(e);
}
// 启动选举过程
startLeaderElection();
super.start();
}
总结
本文讲述了zookeeper的服务启动的源码实现。