一、前言
前面分析了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,其通常是請求處理鏈的最後一個處理器,而對於請求處理鏈部分的分析也就到這裡,還有其他的處理器再使用時再進行分析,也謝謝各位園友觀看~