FinalRequestProcessor是责任链中的最后一个请求处理器,负责把已经commit的写操作(事务)应用到内存数据库中去,对于读操作则从本机中读取数据并返回给client。创建客户端请求的响应。
FinalRequestProcessor是一个同步处理的processor,主要的处理逻辑就在方法processRequest中:
如果request.hdr != null,则表明request是写操作,则调用zks.processTxn(hdr,
txn)来把request关联的写操作执行到内存数据库中如果是写操作,则调用zks.getZKDatabase().addCommittedProposal(request);
把request加入到ZKDatabase.committedLog队列中,这个队列主要是为了快速和follower同步而保留的,详见zookeeper leader和learner的数据同步。为各类操作准备响应数据,对于写操作则根据processTxn的结果来回复,如果是读操作,则读取内存中的状态 发送响应数据给client
public void processRequest(Request request) {
ProcessTxnResult rc = null;
synchronized (zks.outstandingChanges) {
while (!zks.outstandingChanges.isEmpty()
&& zks.outstandingChanges.get(0).zxid <= request.zxid) {
ChangeRecord cr = zks.outstandingChanges.remove(0);
if (cr.zxid < request.zxid) {
LOG.warn("Zxid outstanding "
+ cr.zxid
+ " is less than current " + request.zxid);
}
if (zks.outstandingChangesForPath.get(cr.path) == cr) {
zks.outstandingChangesForPath.remove(cr.path);
}
}
if (request.hdr != null) {
TxnHeader hdr = request.hdr;
Record txn = request.txn;
rc = zks.processTxn(hdr, txn);//把事务请求应用到内存数据库
}
// do not add non quorum packets to the queue.
if (Request.isQuorum(request.type)) {
//如果是事务请求,则把request加入到ZKDatabase.committedLog队列中
zks.getZKDatabase().addCommittedProposal(request);
}
....
}
接下来来看一下如何提交到内存数据库zks.processTxn(hdr, txn);
public ProcessTxnResult processTxn(TxnHeader hdr, Record txn) {
....
rc = getZKDatabase().processTxn(hdr, txn);
....
}
public ProcessTxnResult processTxn(TxnHeader hdr, Record txn) {
return dataTree.processTxn(hdr, txn);
}
public ProcessTxnResult processTxn(TxnHeader header, Record txn)
{
ProcessTxnResult rc = new ProcessTxnResult();
....
if (rc.zxid > lastProcessedZxid) {
lastProcessedZxid = rc.zxid;
}
....
}
我关心的重点是提交到数据库的时候会不会记日志来记录当前服务器已经提交的事务zxid呢?
由上面的源码分析来看并不会记日志,仅仅只是在内存中修改了lastProcessedZxid,代表最后一个提交的事务zxid,并没有持久化,这样的好处就是快,因为不用每次提交都写日志。
那么如果机器突然挂掉重启服务后怎么知道上一次提交到哪里呢?将在下一篇zookeeper服务器初始化的过程中分析。