一、前言

  前面分析了SyncReqeustProcessor,接着分析请求处理链中最后的一个处理器FinalRequestProcessor。

二、FinalRequestProcessor源码分析

  2.1 类的继承关系  

public class FinalRequestProcessor implements RequestProcessor {}

  说明:FinalRequestProcessor只实现了RequestProcessor接口,其需要实现processRequest方法和shutdown方法。

  2.2 类的属性 

public class FinalRequestProcessor implements RequestProcessor {
private static final Logger LOG = LoggerFactory.getLogger(FinalRequestProcessor.class); // ZooKeeper服务器
ZooKeeperServer zks;
}

  说明:其核心属性为zks,表示Zookeeper服务器,可以通过zks访问到Zookeeper内存数据库。

  2.3 类的构造函数

    public FinalRequestProcessor(ZooKeeperServer zks) {
this.zks = zks;
}

  2.4 核心函数分析

  1. processRequest 

    public void processRequest(Request request) {
if (LOG.isDebugEnabled()) {
LOG.debug("Processing request:: " + request);
}
// request.addRQRec(">final");
long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK;
if (request.type == OpCode.ping) { // 请求类型为PING
traceMask = ZooTrace.SERVER_PING_TRACE_MASK;
}
if (LOG.isTraceEnabled()) {
ZooTrace.logRequest(LOG, traceMask, 'E', request, "");
}
ProcessTxnResult rc = null;
synchronized (zks.outstandingChanges) { // 同步块
while (!zks.outstandingChanges.isEmpty()
&& zks.outstandingChanges.get(0).zxid <= request.zxid) { // outstandingChanges不为空且首个元素的zxid小于请求的zxid
// 移除首个元素
ChangeRecord cr = zks.outstandingChanges.remove(0);
if (cr.zxid < request.zxid) { // 若Record的zxid小于请求的zxid
LOG.warn("Zxid outstanding "
+ cr.zxid
+ " is less than current " + request.zxid);
}
if (zks.outstandingChangesForPath.get(cr.path) == cr) { // 根据路径得到Record并判断是否为cr
// 移除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)) { // 只将quorum包(事务性请求)添加进队列
zks.getZKDatabase().addCommittedProposal(request);
}
} if (request.hdr != null && request.hdr.getType() == OpCode.closeSession) { // 请求头不为空并且请求类型为关闭会话
ServerCnxnFactory scxn = zks.getServerCnxnFactory();
// this might be possible since
// we might just be playing diffs from the leader
if (scxn != null && request.cnxn == null) { //
// calling this if we have the cnxn results in the client's
// close session response being lost - we've already closed
// the session/socket here before we can send the closeSession
// in the switch block below
// 关闭会话
scxn.closeSession(request.sessionId);
return;
}
} if (request.cnxn == null) { // 请求的cnxn为空,直接返回
return;
}
ServerCnxn cnxn = request.cnxn; String lastOp = "NA";
zks.decInProcess();
Code err = Code.OK;
Record rsp = null;
boolean closeSession = false;
try {
if (request.hdr != null && request.hdr.getType() == OpCode.error) {
throw KeeperException.create(KeeperException.Code.get((
(ErrorTxn) request.txn).getErr()));
} KeeperException ke = request.getException();
if (ke != null && request.type != OpCode.multi) {
throw ke;
} if (LOG.isDebugEnabled()) {
LOG.debug("{}",request);
}
switch (request.type) {
case OpCode.ping: { // PING请求
// 更新延迟
zks.serverStats().updateLatency(request.createTime); lastOp = "PING";
// 更新响应的状态
cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp,
request.createTime, System.currentTimeMillis());
// 设置响应
cnxn.sendResponse(new ReplyHeader(-2,
zks.getZKDatabase().getDataTreeLastProcessedZxid(), 0), null, "response");
return;
}
case OpCode.createSession: { // 创建会话请求
// 更新延迟
zks.serverStats().updateLatency(request.createTime); lastOp = "SESS";
// 更新响应的状态
cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp,
request.createTime, System.currentTimeMillis());
// 结束会话初始化
zks.finishSessionInit(request.cnxn, true);
return;
}
case OpCode.multi: { // 多重操作 lastOp = "MULT";
rsp = new MultiResponse() ; for (ProcessTxnResult subTxnResult : rc.multiResult) { // 遍历多重操作结果 OpResult subResult ; switch (subTxnResult.type) { // 确定每个操作类型
case OpCode.check: // 检查
subResult = new CheckResult();
break;
case OpCode.create: // 创建
subResult = new CreateResult(subTxnResult.path);
break;
case OpCode.delete: // 删除
subResult = new DeleteResult();
break;
case OpCode.setData: // 设置数据
subResult = new SetDataResult(subTxnResult.stat);
break;
case OpCode.error: // 错误
subResult = new ErrorResult(subTxnResult.err) ;
break;
default:
throw new IOException("Invalid type of op");
}
// 添加至响应结果集中
((MultiResponse)rsp).add(subResult);
} break;
}
case OpCode.create: { // 创建
lastOp = "CREA";
// 创建响应
rsp = new CreateResponse(rc.path);
err = Code.get(rc.err);
break;
}
case OpCode.delete: { // 删除
lastOp = "DELE";
err = Code.get(rc.err);
break;
}
case OpCode.setData: { // 设置数据
lastOp = "SETD";
rsp = new SetDataResponse(rc.stat);
err = Code.get(rc.err);
break;
}
case OpCode.setACL: { // 设置ACL
lastOp = "SETA";
rsp = new SetACLResponse(rc.stat);
err = Code.get(rc.err);
break;
}
case OpCode.closeSession: { // 关闭会话
lastOp = "CLOS";
closeSession = true;
err = Code.get(rc.err);
break;
}
case OpCode.sync: { // 同步
lastOp = "SYNC";
SyncRequest syncRequest = new SyncRequest();
ByteBufferInputStream.byteBuffer2Record(request.request,
syncRequest);
rsp = new SyncResponse(syncRequest.getPath());
break;
}
case OpCode.check: { // 检查
lastOp = "CHEC";
rsp = new SetDataResponse(rc.stat);
err = Code.get(rc.err);
break;
}
case OpCode.exists: { // 存在性判断
lastOp = "EXIS";
// TODO we need to figure out the security requirement for this!
ExistsRequest existsRequest = new ExistsRequest();
// 将byteBuffer转化为Record
ByteBufferInputStream.byteBuffer2Record(request.request,
existsRequest);
String path = existsRequest.getPath();
if (path.indexOf('\0') != -1) {
throw new KeeperException.BadArgumentsException();
}
Stat stat = zks.getZKDatabase().statNode(path, existsRequest
.getWatch() ? cnxn : null);
rsp = new ExistsResponse(stat);
break;
}
case OpCode.getData: { // 获取数据
lastOp = "GETD";
GetDataRequest getDataRequest = new GetDataRequest();
ByteBufferInputStream.byteBuffer2Record(request.request,
getDataRequest);
DataNode n = zks.getZKDatabase().getNode(getDataRequest.getPath());
if (n == null) {
throw new KeeperException.NoNodeException();
}
Long aclL;
synchronized(n) {
aclL = n.acl;
}
PrepRequestProcessor.checkACL(zks, zks.getZKDatabase().convertLong(aclL),
ZooDefs.Perms.READ,
request.authInfo);
Stat stat = new Stat();
byte b[] = zks.getZKDatabase().getData(getDataRequest.getPath(), stat,
getDataRequest.getWatch() ? cnxn : null);
rsp = new GetDataResponse(b, stat);
break;
}
case OpCode.setWatches: { // 设置watch
lastOp = "SETW";
SetWatches setWatches = new SetWatches();
// XXX We really should NOT need this!!!!
request.request.rewind();
ByteBufferInputStream.byteBuffer2Record(request.request, setWatches);
long relativeZxid = setWatches.getRelativeZxid();
zks.getZKDatabase().setWatches(relativeZxid,
setWatches.getDataWatches(),
setWatches.getExistWatches(),
setWatches.getChildWatches(), cnxn);
break;
}
case OpCode.getACL: { // 获取ACL
lastOp = "GETA";
GetACLRequest getACLRequest = new GetACLRequest();
ByteBufferInputStream.byteBuffer2Record(request.request,
getACLRequest);
Stat stat = new Stat();
List<ACL> acl =
zks.getZKDatabase().getACL(getACLRequest.getPath(), stat);
rsp = new GetACLResponse(acl, stat);
break;
}
case OpCode.getChildren: { // 获取子节点
lastOp = "GETC";
GetChildrenRequest getChildrenRequest = new GetChildrenRequest();
ByteBufferInputStream.byteBuffer2Record(request.request,
getChildrenRequest);
DataNode n = zks.getZKDatabase().getNode(getChildrenRequest.getPath());
if (n == null) {
throw new KeeperException.NoNodeException();
}
Long aclG;
synchronized(n) {
aclG = n.acl; }
PrepRequestProcessor.checkACL(zks, zks.getZKDatabase().convertLong(aclG),
ZooDefs.Perms.READ,
request.authInfo);
List<String> children = zks.getZKDatabase().getChildren(
getChildrenRequest.getPath(), null, getChildrenRequest
.getWatch() ? cnxn : null);
rsp = new GetChildrenResponse(children);
break;
}
case OpCode.getChildren2: {
lastOp = "GETC";
GetChildren2Request getChildren2Request = new GetChildren2Request();
ByteBufferInputStream.byteBuffer2Record(request.request,
getChildren2Request);
Stat stat = new Stat();
DataNode n = zks.getZKDatabase().getNode(getChildren2Request.getPath());
if (n == null) {
throw new KeeperException.NoNodeException();
}
Long aclG;
synchronized(n) {
aclG = n.acl;
}
PrepRequestProcessor.checkACL(zks, zks.getZKDatabase().convertLong(aclG),
ZooDefs.Perms.READ,
request.authInfo);
List<String> children = zks.getZKDatabase().getChildren(
getChildren2Request.getPath(), stat, getChildren2Request
.getWatch() ? cnxn : null);
rsp = new GetChildren2Response(children, stat);
break;
}
}
} catch (SessionMovedException e) {
// session moved is a connection level error, we need to tear
// down the connection otw ZOOKEEPER-710 might happen
// ie client on slow follower starts to renew session, fails
// before this completes, then tries the fast follower (leader)
// and is successful, however the initial renew is then
// successfully fwd/processed by the leader and as a result
// the client and leader disagree on where the client is most
// recently attached (and therefore invalid SESSION MOVED generated)
cnxn.sendCloseSession();
return;
} catch (KeeperException e) {
err = e.code();
} catch (Exception e) {
// log at error level as we are returning a marshalling
// error to the user
LOG.error("Failed to process " + request, e);
StringBuilder sb = new StringBuilder();
ByteBuffer bb = request.request;
bb.rewind();
while (bb.hasRemaining()) {
sb.append(Integer.toHexString(bb.get() & 0xff));
}
LOG.error("Dumping request buffer: 0x" + sb.toString());
err = Code.MARSHALLINGERROR;
} long lastZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid();
ReplyHeader hdr =
new ReplyHeader(request.cxid, lastZxid, err.intValue()); zks.serverStats().updateLatency(request.createTime);
cnxn.updateStatsForResponse(request.cxid, lastZxid, lastOp,
request.createTime, System.currentTimeMillis()); try {
cnxn.sendResponse(hdr, rsp, "response");
if (closeSession) {
cnxn.sendCloseSession();
}
} catch (IOException e) {
LOG.error("FIXMSG",e);
}
}

  说明:对于processRequest函数,进行分段分析  

        if (LOG.isDebugEnabled()) {
LOG.debug("Processing request:: " + request);
}
// request.addRQRec(">final");
long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK;
if (request.type == OpCode.ping) { // 请求类型为PING
traceMask = ZooTrace.SERVER_PING_TRACE_MASK;
}
if (LOG.isTraceEnabled()) {
ZooTrace.logRequest(LOG, traceMask, 'E', request, "");
}

  说明:可以看到其主要作用是判断是否为PING请求,同时会根据LOG的设置确定是否进行日志记录,接着下面代码

synchronized (zks.outstandingChanges) { // 同步块
while (!zks.outstandingChanges.isEmpty()
&& zks.outstandingChanges.get(0).zxid <= request.zxid) { // outstandingChanges不为空且首个元素的zxid小于等于请求的zxid
// 移除首个元素
ChangeRecord cr = zks.outstandingChanges.remove(0);
if (cr.zxid < request.zxid) { // 若Record的zxid小于请求的zxid
LOG.warn("Zxid outstanding "
+ cr.zxid
+ " is less than current " + request.zxid);
}
if (zks.outstandingChangesForPath.get(cr.path) == cr) { // 根据路径得到Record并判断是否为cr
// 移除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)) { // 只将quorum包(事务性请求)添加进队列
zks.getZKDatabase().addCommittedProposal(request);
}
}

  说明:同步块处理,当outstandingChanges不为空且其首元素的zxid小于等于请求的zxid时,就会一直从outstandingChanges中取出首元素,并且对outstandingChangesForPath做相应的操作,若请求头不为空,则处理请求。若为事务性请求,则提交到ZooKeeper内存数据库中。对于processTxn函数而言,其最终会调用DataTree的processTxn,即内存数据库结构的DataTree的处理事务函数,而判断是否为事务性请求则是通过调用isQuorum函数,会改变服务器状态的(事务性)请求就是Quorum。之后调用addCommittedProposal函数将请求添加至ZKDatabase的committedLog结构中,方便follower快速同步。

  接下来会根据请求的类型进行相应的操作,如对于PING请求而言,其处理如下  

            case OpCode.ping: { // PING请求
// 更新延迟
zks.serverStats().updateLatency(request.createTime); lastOp = "PING";
// 更新响应的状态
cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp,
request.createTime, System.currentTimeMillis());
// 设置响应
cnxn.sendResponse(new ReplyHeader(-2,
zks.getZKDatabase().getDataTreeLastProcessedZxid(), 0), null, "response");
return;
}

  说明:其首先会根据请求的创建时间来更新Zookeeper服务器的延迟,updateLatency函数中会记录最大延迟、最小延迟、总的延迟和延迟次数。然后更新响应中的状态,如请求创建到响应该请求总共花费的时间、最后的操作类型等。然后设置响应后返回。而对于创建会话请求而言,其处理如下  

            case OpCode.createSession: { // 创建会话请求
// 更新延迟
zks.serverStats().updateLatency(request.createTime); lastOp = "SESS";
// 更新响应的状态
cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp,
request.createTime, System.currentTimeMillis());
// 结束会话初始化
zks.finishSessionInit(request.cnxn, true);
return;
}

  说明:其首先还是会根据请求的创建时间来更新Zookeeper服务器的延迟,然后设置最后的操作类型,然后更新响应的状态,之后调用finishSessionInit函数表示结束会话的初始化。其他请求与此类似,之后会根据其他请求再次更新服务器的延迟,设置响应的状态等,最后使用sendResponse函数将响应发送给请求方,其处理流程如下 

        // 获取最后处理的zxid
long lastZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid();
// 响应头
ReplyHeader hdr =
new ReplyHeader(request.cxid, lastZxid, err.intValue());
// 更新服务器延迟
zks.serverStats().updateLatency(request.createTime);
// 更新状态
cnxn.updateStatsForResponse(request.cxid, lastZxid, lastOp,
request.createTime, System.currentTimeMillis()); try {
// 返回响应
cnxn.sendResponse(hdr, rsp, "response");
if (closeSession) {
// 关闭会话
cnxn.sendCloseSession();
}
} catch (IOException e) {
LOG.error("FIXMSG",e);
}

三、总结

  本篇博文分析了请求处理链的FinalRequestProcessor,其通常是请求处理链的最后一个处理器,而对于请求处理链部分的分析也就到这里,还有其他的处理器再使用时再进行分析,也谢谢各位园友观看~

【Zookeeper】源码分析之请求处理链(四)之FinalRequestProcessor的更多相关文章

  1. 【Zookeeper】源码分析之请求处理链(四)

    一.前言 前面分析了SyncReqeustProcessor,接着分析请求处理链中最后的一个处理器FinalRequestProcessor. 二.FinalRequestProcessor源码分析 ...

  2. 【Zookeeper】源码分析之请求处理链(一)

    一.前言 前面已经分析了Watcher机制的主要代码,现在接着分析Zookeeper中的请求处理链,其是Zookeeper的主要特点之一. 二.总体框图 对于请求处理链而言,所有请求处理器的父接口为R ...

  3. 【Zookeeper】源码分析之请求处理链(二)

    一.前言 前面学习了请求处理链的RequestProcessor父类,接着学习PrepRequestProcessor,其通常是请求处理链的第一个处理器. 二.ZooKeeper源码分析 2.1 类的 ...

  4. 【Zookeeper】源码分析之请求处理链(二)之PrepRequestProcessor

    一.前言 前面学习了请求处理链的RequestProcessor父类,接着学习PrepRequestProcessor,其通常是请求处理链的第一个处理器. 二.PrepRequestProcessor ...

  5. 【Zookeeper】源码分析之请求处理链(三)

    一.前言 在分析了PrepRequestProcessor处理器后,接着来分析SyncRequestProcessor,该处理器将请求存入磁盘,其将请求批量的存入磁盘以提高效率,请求在写入磁盘之前是不 ...

  6. 【Zookeeper】源码分析之请求处理链(三)之SyncRequestProcessor

    一.前言 在分析了PrepRequestProcessor处理器后,接着来分析SyncRequestProcessor,该处理器将请求存入磁盘,其将请求批量的存入磁盘以提高效率,请求在写入磁盘之前是不 ...

  7. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  8. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  9. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

随机推荐

  1. 048 hive运行的相关配置

    一:执行SQL的方式 1.配置的键值 2.minimal下运行fetch 3.设定hive.fetch.task.conversion=more 4.在more下运行fetch 二:虚拟列 一共三个虚 ...

  2. 全排列问题(递归&非递归&STL函数)

    问题描述: 打印输出1-9的所有全排序列,或者打印输出a-d的全排列. 思路分析: 将每个元素放到余下n-1个元素组成的队列最前方,然后对剩余元素进行全排列,依次递归下去. 比如:1 2 3 为例首先 ...

  3. Socket进程通信机制

    1.Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄. 2.应用程序通过套接字向网络发出请求或者应答网络请求. 3.Socket既不是一个程序,也不是一种协议,其只是操作系统 ...

  4. hdu1698 Just a Hook 【区间修改】(模板题)

    题目链接:https://vjudge.net/contest/182746#problem/E 题目大意: 一段线段由n条小线段组成,每次操作把一个区间的小线段变成金银铜之一(金的价值为3,银为2, ...

  5. 20172302 《Java软件结构与数据结构》第六周学习总结

    2018年学习总结博客总目录:第一周 第二周 第三周 第四周 第五周 第六周 教材学习内容总结 1.树的概述及基本概念 (1)树是一种非线性数据结构,其中的元素被组织成了一个层次结构. (2)树由一个 ...

  6. 进程间通信(IPC)

    1.什么是进程间通信 通俗来讲,进程间通信就是:多个进程之间的数据交互 进程都有自己独立的虚拟地址空间,导致进程之间的数据交互变得十分困难,通信复杂了,但是安全性提高了: 进程间通信的本质:多个进程之 ...

  7. Playmaker全面实践教程之playMaker编辑器

    Playmaker全面实践教程之playMaker编辑器 playMaker编辑器 playMaker编辑器是制作状态机的主要视图,如图1-23所示.只有熟悉此视图,读者才能更加快捷的使用Playma ...

  8. C++学习笔记44:继承与派生

    类的组合,类的继承 类的组合(汽车类,轮子类,此时可以把轮子类组合到汽车类:) 类的继承(交通工具类,汽车类,此时汽车类可以派生自交通工具类:) 组合:常用描述has a.. 继承:常用描述is a ...

  9. while 、函数、参数

    while 循环 一个循环语句 表示当某个条件成立时就循环 不知道具体循环次数,但能确定循环的成立条件的时候用while循环 while 条件表达式: 语句块 例1 :如果年利率为6.7%,本利是每年 ...

  10. zabbix 添加用户 配置权限

    点击 Administration → Users 添加新用户,单击 Create user  ,在新的用户表单中,确保将用户添加到现有用户组之一,例如“Zabbix administators”. ...