我们在win下启动是从D:\tools\zookeeper\zookeeper-3.4.14\bin下的zkServer.cmd,打开这个脚本,可以看到其实就是运行org.apache.zookeeper.server.quorum.QuorumPeerMain这个类,先看这个类的main方法
81 main.initializeAndRun(args);
=>114 runFromConfig(config); //如果配置了集群,解析集群相关信息
=>119 ZooKeeperServerMain.main(args); //单机版服务器,本次主要看这个方法
这个main方法中,分为两个分支,我们主要看ZooKeeperServerMain.main(args);
55 main.initializeAndRun(args);
=>75-87 创建一个配置对象config用于保存配置信息
=>89 runFromConfig(config);
服务端启动,先读取配置文件,保存到内存中,然后执行runFromConfig(config);跟进
105-117 new ZooKeeperServer(); //创建zk服务端,并设置其参数
118 cnxnFactory = ServerCnxnFactory.createFactory(); //创建访问协议,默认NIO
119 cnxnFactory.configure方法 //创建zookeeperThread,并启动一个socket等待客户端的连接
121 cnxnFactory.startup(zkServer);
先创建ZookeeperServer对象,再创建cnxnFactory对象(默认NIO,也可以通过配置文件使用Netty)
调用cnxnFactory.configure方法时,会创建zookeeperThread,并启动一个socket等待客户端的连接
最后通过cnxnFactory.startup(zkServer); 跟进这个会提示有两个子类,我们选择默认方式NIOServerCnxnFactory
116 start(); //启动了本类的run方法
117 setZooKeeperServer(zks);
118 zks.startdata();
119 zks.startup();
分别来看这四个方法
1) start();
作用:启动了本类(NIOServerCnxnFactory)的run方法,这个run方法一直在while循环判断socket是否关闭,没有的话则
=>226 c.doIO(k); //当监听到客户端发来的读或写的请求
==>245 if (k.isReadable()) //如果是读请求
==>246 sock.read(incomingBuffer); //从socket读取数据到incomingBuffer
==>264 readPayload(); //先判断是否初始化过,初始化过的话就读请求
===>393 zkServer.processPacket(this, incomingBuffer);
====>1023 submitRequest(si);
=====>750 firstProcessor.processRequest(si); //PrepRequestProcessor这个类的processRequest,最终将读请求放到submittedRequest这个队列里
==>273 if (k.isWritable()) //如果是写请求
==>273-364 把写请求放入outgoingBuffers
也就是说,通过NIOServerCnxnFactory的run方法,服务端对客户端的请求进行监听,当有请求时,根据读、写请求分别进行处理
2) setZooKeeperServer(zks);//没什么好说的,只是设置一下
3) zks.startdata();//加载日志和快照
4) zks.startup();
=>410 进入ZooKeeperServer的startup
=>414 startSessionTracker();//查看session是否超时,别的没做什么
=>415 setupRequestProcessors();//重点
其中415行,是重点,代码如下
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();
}
创建了三个对象PrepRequestProcessor、SyncRequestProcessor、FinalRequestProcessor,通过链式调用,我们按照调用顺序分别看下这三个对象的作用:
首先、调用PrepRequestProcessor的run方法(真正处理客户端请求)
123 submittedRequests.take();//获取一个请求
134 pRequest(request);//处理客户端的请求
=>540 pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest, true);//根据操作类型,进行相应的处理
==>375 addChangeRecord(parentRecord); //处理完成后,将处理逻辑加入outstandingChanges队列
至此PrepRequestProcessor的run方法已看完,主要是处理客户端的请求,然后将处理结果加入到outstandingChanges队列
其次、再看SyncRequestProcessor的run方法(记录内存,及快照)
127 si = queuedRequests.take();//获取一个请求
140-160 记录日志,如果超过配置的参数snapCount / 2 + randRoll则记录快照
169 nextProcessor.processRequest(si); //进入FinalRequestProcessor
最后、再看FinalRequestProcessor.processRequest(si)
117 rc = zks.processTxn(hdr, txn);
=>1075 rc = getZKDatabase().processTxn(hdr, txn); //zk的内存数据库,将请求保存的内存(客户端请求处理完后,日志,快照(超过),内存都有)
==>700-760 根据不同请求类型,操作内存数据库
==>807 ProcessTxnResult subRc = processTxn(subHdr, record); //开始响应客户端
至此,单机版服务器结束