单机版zookeeper的启动类为org.apache.zookeeper.server.ZooKeeperMain.java
ZooKeeper启动参数有两种配置方式:
方式1:main方法中传入四个参数,其中前两参数必填,后两个参数可选。分别为:对客户端暴露的端口clientPortAddress, 存放事务记录、内存树快照记录的dataDir, 用于指定seesion检查时间间隔的tickTime, 控制最大客户端连接数的maxClientCnxns。
方式2:给出启动参数配置文件路径,当args启动参数只有一个时,ZooKeeperServerMain 中main方法,会认为传入了配置文件路径,默认情况下该参数是传conf目录中的zoo.cfg。
protected void initializeAndRun(String[] args)
throws ConfigException, IOException, AdminServerException
{
try {
ManagedUtil.registerLog4jMBeans();
} catch (JMException e) {
LOG.warn("Unable to register log4j JMX control", e);
}
ServerConfig config = new ServerConfig();
if (args.length == 1) {
config.parse(args[0]);///对应方式2,传入配置文件地址
} else {
config.parse(args);///对应方式1,传入启动参数
}
runFromConfig(config);
}
ZooKeeperServerMain中,真正实现启动逻辑的是runFromConfig方法。
启动流程:
1:初始化一个事物、内存树快照记录器对象。
2:初始化一个单机版ZooKeeperServer对象,主要功能是创建处理客户端传来的request的处理链,当然也包括记录其他一些配置,ZKDatabase、maxSessionTimeout等。
3:初始化一个CountDownLatch对象shutDownLatch,用于线程之间协作,其主要有两个方法,countDown(), await(),countDowan() 方法使计数减一。调用await()方法会一直阻塞,直到计数变为0。在这里shutDownLatch负责在发生错误时关闭zookeeper服务。
4:初始化自带的管理后台server,默认容器是jetty。可以在配置文件中,配置
zookeeper.admin.enableServer = false
让zookeeper不会启动管理后台服务。
5:创建负责真正和客户端连接的ServerCnxnFactory对象cnxnFactory,在这里,可以通过配置选择使用JAVA自带的异步IO负责连接,也可以通过配置zookeeper.serverCnxnFactory,选择使用netty作为连接服务端,创建后,会将第2步的ZooKeeperServer对象,作为参数传递到cnxnFactory,然后启动服务。
6:根据配置决定是否初始化负责安全连接的secureCnxnFactory。
7:初始化ContainerManager对象,负责管理container ZNodes。
代码如下:
public void runFromConfig(ServerConfig config) throws IOException, AdminServerException {
LOG.info("Starting server");
FileTxnSnapLog txnLog = null;
try {
// Note that this thread isn't going to be doing anything else,
// so rather than spawning another thread, we will just call
// run() in this thread.
// create a file logger url from the command line args
txnLog = new FileTxnSnapLog(config.dataLogDir, config.dataDir);
final ZooKeeperServer zkServer = new ZooKeeperServer(txnLog,
config.tickTime, config.minSessionTimeout, config.maxSessionTimeout, null);
// Registers shutdown handler which will be used to know the
// server error or shutdown state changes.
final CountDownLatch shutdownLatch = new CountDownLatch(1);
zkServer.registerServerShutdownHandler(
new ZooKeeperServerShutdownHandler(shutdownLatch));
// Start Admin server
adminServer = AdminServerFactory.createAdminServer();
adminServer.setZooKeeperServer(zkServer);
adminServer.start();
boolean needStartZKServer = true;
if (config.getClientPortAddress() != null) {
cnxnFactory = ServerCnxnFactory.createFactory();
cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), false);
cnxnFactory.startup(zkServer);
// zkServer has been started. So we don't need to start it again in secureCnxnFactory.
needStartZKServer = false;
}
if (config.getSecureClientPortAddress() != null) {
secureCnxnFactory = ServerCnxnFactory.createFactory();
secureCnxnFactory.configure(config.getSecureClientPortAddress(), config.getMaxClientCnxns(), true);
secureCnxnFactory.startup(zkServer, needStartZKServer);
}
containerManager = new ContainerManager(zkServer.getZKDatabase(), zkServer.firstProcessor,
Integer.getInteger("znode.container.checkIntervalMs", (int) TimeUnit.MINUTES.toMillis(1)),
Integer.getInteger("znode.container.maxPerMinute", 10000)
);
containerManager.start();
// Watch status of ZooKeeper server. It will do a graceful shutdown
// if the server is not running or hits an internal error.
shutdownLatch.await();
shutdown();
if (cnxnFactory != null) {
cnxnFactory.join();
}
if (secureCnxnFactory != null) {
secureCnxnFactory.join();
}
if (zkServer.canShutdown()) {
zkServer.shutdown();
}
} catch (InterruptedException e) {
// warn, but generally this is ok
LOG.warn("Server interrupted", e);
} finally {
if (txnLog != null) {
txnLog.close();
}
}
}
单机版ZooKeeper,启动类是ZooKeeperServerMain。最终调用ZooKeeperServer的startup()方法来处理request。可以通过配置选择使用JAVA自带NIO或者netty作为异步连接服务端。