预启动
1、统一由 QuorumPeerMain 作为启动类。
无论是单机版还是集群模式启动Zookeeper服务器,QuorumPeerMain 都作为启动入口。
2、解析配置文件Zoo.cfg
首先会创建 QuorumPeerConfig 并解析配置文件(zoo.cfg)。该文件包括 tickTime 、dataDir 和 clientPort等参数。
3、创建并启动历史文件清理器 DatadirCleanupManager
4、判断是单机模式还是集群模式,此处分析单机版模式,会委托给 ZooKeeperServerMain 进行处理
5、创建ServerConfig并进行配置文件的再次解析
public class ZooKeeperServerMain {
public static void main(String[] args) {
ZooKeeperServerMain main = new ZooKeeperServerMain();
//省略部分代码
main.initializeAndRun(args);
}
protected void initializeAndRun(String[] args)
throws ConfigException, IOException
{
ServerConfig config = new ServerConfig();
//配置文件的解析入口
if (args.length == 1) {
config.parse(args[0]);
} else {
config.parse(args);
}
runFromConfig(config);
}
}
当args.length == 1时,其实args[0] 就表示配置文件的path,内部会委托给 QuorumPeerConfig 进行解析
当args.length != 1 时
/**
* Parse arguments for server configuration
* @param args clientPort dataDir and optional tickTime
* @return ServerConfig configured wrt arguments
* @throws IllegalArgumentException on invalid usage
*/
public void parse(String[] args) {
if (args.length < 2 || args.length > 4) {
throw new IllegalArgumentException("Invalid args:"
+ Arrays.toString(args));
}
clientPortAddress = new InetSocketAddress(Integer.parseInt(args[0]));
dataDir = args[1];
dataLogDir = dataDir;
if (args.length == 3) {
tickTime = Integer.parseInt(args[2]);
}
if (args.length == 4) {
maxClientCnxns = Integer.parseInt(args[3]);
}
}
6、创建服务器实例ZookeeperServer
ZookeeperServer是单机版Zookeeper服务端最核心的实体类。
初始化阶段
1、创建服务器的统计器ServerStats
ServerStats 是 Zookeeper 服务器运行时统计器,包含了最基本的运行时信息。
/**
* Basic Server Statistics
* //从Zookeeper启动开始或者是最近一次重置服务端统计信息 之后开始进行统计
*/
public class ServerStats {
//服务端向客户端发送的响应包数
private long packetsSent;
//服务端接收到的来自客户端的请求包数
private long packetsReceived;
//服务端请求处理最 大延时
private long maxLatency;
//服务端请求处理最 小延时
private long minLatency = Long.MAX_VALUE;
//服务端请求处理 总延时
private long totalLatency = 0;
//服务端处理的客户端请求总数
private long count = 0;
//省略部分代码
}
2、创建Zookeeper数据管理器 FileTxnSnapLog
FileTxnSnapLog是Zookeeper上层服务器和底层数据存储之间的对接层,正如源码中所说 This is a helper class ,提供了一系列操作文件的接口,包括事务日志文件和快照数据文件。根据zoo.cfg里面的dataDir(快照数据文件),dataLogDir(事务日志文件)来创建FileTxnSnapLog
3、设置服务器tickTime和会话超时时间限制
zkServer.setTickTime(config.tickTime);
zkServer.setMinSessionTimeout(config.minSessionTimeout);
zkServer.setMaxSessionTimeout(config.maxSessionTimeout);
4、创建 ServerCnxnFactory
可以通过 zookeeper.serverCnxnFactory 来指定使用NIOServerCnxnFactory 还是 NettyServerCnxnFactory
5、初始化ServerCnxnFactory
Zookeeper首先会初始化一个线程,作为整个ServerCnxnFactory 的主线程,然后再初始化NIO服务器。
public void configure(InetSocketAddress addr, int maxcc) throws IOException {
configureSaslLogin();
thread = new Thread(this, "NIOServerCxn.Factory:" + addr);
thread.setDaemon(true);
maxClientCnxns = maxcc;
this.ss = ServerSocketChannel.open();
ss.socket().setReuseAddress(true);
LOG.info("binding to port " + addr);
ss.socket().bind(addr);
ss.configureBlocking(false);
ss.register(selector, SelectionKey.OP_ACCEPT);
}
6、启动ServerCnxnFactory 主线程
直接调用线程的start方法启动线程
7、恢复本地数据
//调用ZookeeperServer的startdata方法恢复数据
public void startdata()
throws IOException, InterruptedException {
//check to see if zkDb is not null
if (zkDb == null) {
zkDb = new ZKDatabase(this.txnLogFactory);
}
if (!zkDb.isInitialized()) {
loadData();
}
}
8、创建并启动会话管理器
在Zookeeper启动时会创建一个会话管理器SessionTracker
public void startup() {
if (sessionTracker == null) {
createSessionTracker();
}
startSessionTracker();
//省略部分代码
}
9、初始化Zookeeper的请求处理链
Zookeeper请求处理是典型的责任链模式,在Zookeeper服务器上,会有多个请求处理器依次来处理客户端的请求
protected void setupRequestProcessors() {
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
RequestProcessor syncProcessor = new SyncRequestProcessor(this,
finalProcessor);
((SyncRequestProcessor)syncProcessor).start();
firstProcessor = new PrepRequestProcessor(this, syncProcessor);
((PrepRequestProcessor)firstProcessor).start();
}
10、注册JMX服务
registerJMX()
11、注册Zookeeper服务器实例
NIOServerCnxnFactory 类的方法 setZooKeeperServer(zks);
至此,单机版的Zookeeper服务器实例已经启动。