接着上一篇分析zookeeper单机启动类函数
/**
* Run from a ServerConfig.
* @param config ServerConfig to use.
* @throws IOException
* @throws AdminServerException
*/
public void runFromConfig(ServerConfig config) throws IOException, AdminServerException {
LOG.info("Starting server");
FileTxnSnapLog txnLog = null;
try {
try {
metricsProvider = MetricsProviderBootstrap.startMetricsProvider( //指标记录类
config.getMetricsProviderClassName(),
config.getMetricsProviderConfiguration());
} catch (MetricsProviderLifeCycleException error) {
throw new IOException("Cannot boot MetricsProvider " + config.getMetricsProviderClassName(), error);
}
ServerMetrics.metricsProviderInitialized(metricsProvider);
// 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); //创建数据和日志文件类
JvmPauseMonitor jvmPauseMonitor = null;
if (config.jvmPauseMonitorToRun) {
jvmPauseMonitor = new JvmPauseMonitor(config); //jvm暂停进程监控,默认不开启
}
//初始化配置,为zkserver相关线程做相关准备
final ZooKeeperServer zkServer = new ZooKeeperServer(jvmPauseMonitor, txnLog, config.tickTime, config.minSessionTimeout, config.maxSessionTimeout, config.listenBacklog, null, config.initialConfig);
txnLog.setServerStats(zkServer.serverStats()); //重复设置了一遍
// 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)); //当server发生故障,利用CountDownLatch停止主线程阻塞,结束进程
// Start Admin server
adminServer = AdminServerFactory.createAdminServer();
adminServer.setZooKeeperServer(zkServer);
adminServer.start(); //使用adminserver 来获取zookeeper相关指标和信息,封装了命令类,来从zkserver实例(前面zkserver中基本将所有配置类存储到zkserver中)中获取相关信息,集群默认不会开启的
boolean needStartZKServer = true;
if (config.getClientPortAddress() != null) {
cnxnFactory = ServerCnxnFactory.createFactory(); //server连接线程初始化工厂,初始化的是NIOServerCnxnFactory,还是一个netty的工厂类
cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), 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(), config.getClientPortListenBacklog(), 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),
Long.getLong("znode.container.maxNeverUsedIntervalMs", 0)
);
containerManager.start();
ZKAuditProvider.addZKStartStopAuditLog();
// 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(true);
}
} catch (InterruptedException e) {
// warn, but generally this is ok
LOG.warn("Server interrupted", e);
} finally {
if (txnLog != null) {
txnLog.close();
}
if (metricsProvider != null) {
try {
metricsProvider.stop();
} catch (Throwable error) {
LOG.warn("Error while stopping metrics", error);
}
}
}
}
初始化zkserver后,紧接着初始化了一个adminserver,用来访问zk中的指标信息以及一些常用的命令信息,默认端口是8080,主要是将zkserver实例塞进了adminserver,并实例化了一些命令类,来通过zkserver获取相关信息。
然后,下面初始化了一个ServerCnxnFactory类,该类的作用比较重要,主要是配置一个seversocketChanel,默认端口为2181,在一个线程中注册一个accept的selector,异步监听连接的请求,然后轮询分发给一个accept线程池队列,其次是配置一个处理请求的io线程池,处理发过来的请求。连接工厂具体配置源码如下。
@Override
public void configure(InetSocketAddress addr, int maxcc, int backlog, boolean secure) throws IOException {
if (secure) {
throw new UnsupportedOperationException("SSL isn't supported in NIOServerCnxn");
}
configureSaslLogin(); //配置sasl(给予java security),需要满足两个条件,一个是zoo.cfg中的配置sasl,另一个是Jass.conf的配置,还有一个认证方式Kerberos,比sasl更全面
maxClientCnxns = maxcc; //最大的客户端连接数(针对单个客户端Ip),默认60
initMaxCnxns(); //server的连接数限制,默认不限制
sessionlessCnxnTimeout = Integer.getInteger(ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT, 10000); //连接超时时间10s
// We also use the sessionlessCnxnTimeout as expiring interval for
// cnxnExpiryQueue. These don't need to be the same, but the expiring
// interval passed into the ExpiryQueue() constructor below should be
// less than or equal to the timeout.
cnxnExpiryQueue = new ExpiryQueue<NIOServerCnxn>(sessionlessCnxnTimeout);//超时连接队列
expirerThread = new ConnectionExpirerThread(); //关闭超时连接
int numCores = Runtime.getRuntime().availableProcessors();
// 32 cores sweet spot seems to be 4 selector threads
numSelectorThreads = Integer.getInteger(
ZOOKEEPER_NIO_NUM_SELECTOR_THREADS,
Math.max((int) Math.sqrt((float) numCores / 2), 1));//accept线程队列数量
if (numSelectorThreads < 1) {
throw new IOException("numSelectorThreads must be at least 1");
}
numWorkerThreads = Integer.getInteger(ZOOKEEPER_NIO_NUM_WORKER_THREADS, 2 * numCores);//工作线程,处理请求io线程池数量
workerShutdownTimeoutMS = Long.getLong(ZOOKEEPER_NIO_SHUTDOWN_TIMEOUT, 5000); //工作线程超时
String logMsg = "Configuring NIO connection handler with "
+ (sessionlessCnxnTimeout / 1000) + "s sessionless connection timeout, "
+ numSelectorThreads + " selector thread(s), "
+ (numWorkerThreads > 0 ? numWorkerThreads : "no") + " worker threads, and "
+ (directBufferBytes == 0 ? "gathered writes." : ("" + (directBufferBytes / 1024) + " kB direct buffers."));
LOG.info(logMsg);
for (int i = 0; i < numSelectorThreads; ++i) {
selectorThreads.add(new SelectorThread(i)); //初始化工作线程队列
}
listenBacklog = backlog;
this.ss = ServerSocketChannel.open(); //打开nio中serverChanel
ss.socket().setReuseAddress(true);
LOG.info("binding to port {}", addr);
if (listenBacklog == -1) {
ss.socket().bind(addr); //绑定chanel监听地址
} else {
ss.socket().bind(addr, listenBacklog);
}
ss.configureBlocking(false); //设置chaneo为异步非阻塞(io非阻塞,异步监听消息)
acceptThread = new AcceptThread(ss, addr, selectorThreads); //初始化接受监听线程
}
配置完后,启动nio accpet线程和io线程,以及启动zkserver相关线程,这里涉及到的启动端口就是2181,选举端口和心跳端口只在集群中使用。
另外,这里用到的网络模式属于Reactor多线程模型,一个Nio线程接口accpet,一组Nio线程接受read和write。
图片地址: https://blog.csdn.net/weixin_38073885/article/details/90145803
连接类启动函数
@Override
public void startup(ZooKeeperServer zks, boolean startServer) throws IOException, InterruptedException {
start(); //启动Nio
setZooKeeperServer(zks);
if (startServer) {
zks.startdata(); //初始化data数据库,获取默认前100个存储的snapshot数据
zks.startup(); //启动zkserver相关线程
}
}
@Override
public void start() {
stopped = false;
if (workerPool == null) {
workerPool = new WorkerService("NIOWorker", numWorkerThreads, false); //初始化请求处理线程池
}
for (SelectorThread thread : selectorThreads) {
if (thread.getState() == Thread.State.NEW) {
thread.start();//启动Nio io线程池
}
}
// ensure thread is started once and only once
if (acceptThread.getState() == Thread.State.NEW) {
acceptThread.start(); //连接NIO线程启动
}
if (expirerThread.getState() == Thread.State.NEW) {
expirerThread.start(); //管理连接超时线程
}
}