背景
Zookeeper中使用责任链模式处理客户端提交的请求,本文介绍常用的RequestProcessor
如何工作?各线程间如何通信?
注:下文中使用事务代表会改变服务器状态的请求,如创建节点,更新数据
单机版初始化
单机版服务器在初始化ZooKeeperServer
时,创建并启动了请求处理链,代码如下:
/**
* 设置单机版服务器的请求处理链
* {@link PrepRequestProcessor}->{@link SyncRequestProcessor}->{@link FinalRequestProcessor}
*/
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();
}
单机版服务器包含3个请求处理器,从前往后依次为PrepRequestProcessor
->SyncRequestProcessor
->FinalRequestProcessor
PrepRequestProcessor
基本功能
PrepRP
是服务器的请求预处理器,能够识别出当前客户端是否是事务请求,对于事务请求,PrepRP
对其进行一系列预处理,如创建请求事务头,事务体,会话检查,ACL检查等.
由于其检查请求是否是事务请求,因此其代码中存在大量的switch... case ...
,代码重复且庞杂,因此就不贴源码了.
线程通信
PrepRP
继承了Thread
,因此其是一个单独的线程,通过PrepRP.submittedRequests
与其他线程通信,submittedRequests
中存放待处理的请求,调用processRequest()
即可将request添加至submittedRequests
中:
@Override
public void processRequest(Request request) {
submittedRequests.add(request);
}
我们来逆向推下该方法的调用链:
PrepRequestProcessor.processRequest()
<-ZooKeeperServer.submitRequest()
<-ZooKeeperServer.processPacket()
<-NIOServerCnxn.readRequest()
<-NIOServerCnxn.readPayload()
<-NIOServerCnxn.doIO()
不知道你对最后一个方法NIOServerCnxn.doIO()
是否有印象,该方法在worker thread中被调用,用于读取客户端发送的数据,因此submittedRequests
就是worker thread和prepRP thread通信的工具.
SyncRequestProcessor
基本功能
SyncRP
的主要功能就是生成快照文件和将事务写入事务日志中.详见Zookeeper-持久化
线程通信
SyncRP
也继承了Thread
,通过queuedRequests
与prepRP thread通信,调用processRequest()
即可将request添加至queuedRequests
中:
/**
* @param request 将要被处理的request
*/
@Override
public void processRequest(Request request) {
//将request加入请求队列
queuedRequests.add(request);
}
FinalRequestProcessor
基本功能
FinalRP
主要完成两个功能:
- 创建客户端请求的响应
- 将事务应用到内存数据中去
代码较为简单,就不贴源码了.