一、前言
前面學習了請求處理鏈的RequestProcessor父類,接著學習PrepRequestProcessor,其通常是請求處理鏈的第一個處理器。
二、PrepRequestProcessor原始碼分析
2.1 類的繼承關係
public class PrepRequestProcessor extends Thread implements RequestProcessor {}
說明:可以看到PrepRequestProcessor繼承了Thread類並實現了RequestProcessor介面,表示其可以作為執行緒使用。
2.2 類的屬性
public class PrepRequestProcessor extends Thread implements RequestProcessor { // 日誌記錄器 private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessor.class); // 是否跳過ACL,需檢視系統配置 static boolean skipACL; static { skipACL = System.getProperty("zookeeper.skipACL", "no").equals("yes"); if (skipACL) { LOG.info("zookeeper.skipACL==\"yes\", ACL checks will be skipped"); } } /** * this is only for testing purposes. * should never be useed otherwise */ // 僅用作測試使用 private static boolean failCreate = false; // 已提交請求佇列 LinkedBlockingQueue<Request> submittedRequests = new LinkedBlockingQueue<Request>(); // 下個處理器 RequestProcessor nextProcessor; // Zookeeper伺服器 ZooKeeperServer zks; }
說明:類的核心屬性有submittedRequests和nextProcessor,前者表示已經提交的請求,而後者表示提交的下個處理器。
2.3 類的建構函式
public PrepRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { // 呼叫父類Thread建構函式 super("ProcessThread(sid:" + zks.getServerId() + " cport:" + zks.getClientPort() + "):"); // 類屬性賦值 this.nextProcessor = nextProcessor; this.zks = zks; }
說明:該建構函式首先會呼叫父類Thread的建構函式,然後利用建構函式引數給nextProcessor和zks賦值。
2.4 核心函式分析
1. run函式
public void run() { try { while (true) { // 無限迴圈 // 從佇列中取出一個請求 Request request = submittedRequests.take(); // long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK; if (request.type == OpCode.ping) { // 請求型別為PING traceMask = ZooTrace.CLIENT_PING_TRACE_MASK; } if (LOG.isTraceEnabled()) { // 是否可追蹤 ZooTrace.logRequest(LOG, traceMask, 'P', request, ""); } if (Request.requestOfDeath == request) { // 在關閉處理器之後,會新增requestOfDeath,表示關閉後不再處理請求 break; } // 呼叫pRequest函式 pRequest(request); } } catch (InterruptedException e) { // 中斷異常 LOG.error("Unexpected interruption", e); } catch (RequestProcessorException e) { // 請求處理異常 if (e.getCause() instanceof XidRolloverException) { LOG.info(e.getCause().getMessage()); } LOG.error("Unexpected exception", e); } catch (Exception e) { // 其他異常 LOG.error("Unexpected exception", e); } LOG.info("PrepRequestProcessor exited loop!"); }
說明:run函式是對Thread類run函式的重寫,其核心邏輯相對簡單,即不斷從佇列中取出request進行處理,其會呼叫pRequest函式,其原始碼如下
protected void pRequest(Request request) throws RequestProcessorException { // LOG.info("Prep>>> cxid = " + request.cxid + " type = " + // request.type + " id = 0x" + Long.toHexString(request.sessionId)); // 將請求的hdr和txn設定為null request.hdr = null; request.txn = null; try { switch (request.type) { // 確定請求型別 case OpCode.create: // 建立節點請求 // 新生建立節點請求 CreateRequest createRequest = new CreateRequest(); // 處理請求 pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest, true); break; case OpCode.delete: // 刪除節點請求 // 新生刪除節點請求 DeleteRequest deleteRequest = new DeleteRequest(); // 處理請求 pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest, true); break; case OpCode.setData: // 設定資料請求 // 新生設定資料請求 SetDataRequest setDataRequest = new SetDataRequest(); // 處理請求 pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest, true); break; case OpCode.setACL: // 設定ACL請求 // 新生設定ACL請求 SetACLRequest setAclRequest = new SetACLRequest(); // 處理請求 pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest, true); break; case OpCode.check: // 檢查版本請求 // 新生檢查版本請求 CheckVersionRequest checkRequest = new CheckVersionRequest(); // 處理請求 pRequest2Txn(request.type, zks.getNextZxid(), request, checkRequest, true); break; case OpCode.multi: // 多重請求 // 新生多重請求 MultiTransactionRecord multiRequest = new MultiTransactionRecord(); try { // 將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, multiRequest); } catch(IOException e) { // 出現異常則重新生成Txn頭 request.hdr = new TxnHeader(request.sessionId, request.cxid, zks.getNextZxid(), zks.getTime(), OpCode.multi); throw e; } List<Txn> txns = new ArrayList<Txn>(); //Each op in a multi-op must have the same zxid! long zxid = zks.getNextZxid(); KeeperException ke = null; //Store off current pending change records in case we need to rollback // 儲存當前掛起的更改記錄,以防我們需要回滾 HashMap<String, ChangeRecord> pendingChanges = getPendingChanges(multiRequest); int index = 0; for(Op op: multiRequest) { // 遍歷請求 Record subrequest = op.toRequestRecord() ; /* If we've already failed one of the ops, don't bother * trying the rest as we know it's going to fail and it * would be confusing in the logfiles. */ if (ke != null) { // 發生了異常 request.hdr.setType(OpCode.error); request.txn = new ErrorTxn(Code.RUNTIMEINCONSISTENCY.intValue()); } /* Prep the request and convert to a Txn */ else { // 未發生異常 try { // 將Request轉化為Txn pRequest2Txn(op.getType(), zxid, request, subrequest, false); } catch (KeeperException e) { // 轉化發生異常 if (ke == null) { ke = e; } // 設定請求頭的型別 request.hdr.setType(OpCode.error); // 設定請求的Txn request.txn = new ErrorTxn(e.code().intValue()); LOG.info("Got user-level KeeperException when processing " + request.toString() + " aborting remaining multi ops." + " Error Path:" + e.getPath() + " Error:" + e.getMessage()); // 設定異常 request.setException(e); /* Rollback change records from failed multi-op */ // 從多重操作中回滾更改記錄 rollbackPendingChanges(zxid, pendingChanges); } } //FIXME: I don't want to have to serialize it here and then // immediately deserialize in next processor. But I'm // not sure how else to get the txn stored into our list. // 序列化 ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); request.txn.serialize(boa, "request") ; ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); txns.add(new Txn(request.hdr.getType(), bb.array())); index++; } // 給請求頭賦值 request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, zks.getTime(), request.type); // 設定請求的Txn request.txn = new MultiTxn(txns); break; //create/close session don't require request record case OpCode.createSession: // 建立會話請求 case OpCode.closeSession: // 關閉會話請求 pRequest2Txn(request.type, zks.getNextZxid(), request, null, true); break; //All the rest don't need to create a Txn - just verify session // 所有以下請求只需驗證會話即可 case OpCode.sync: case OpCode.exists: case OpCode.getData: case OpCode.getACL: case OpCode.getChildren: case OpCode.getChildren2: case OpCode.ping: case OpCode.setWatches: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); break; } } catch (KeeperException e) { // 發生KeeperException異常 if (request.hdr != null) { request.hdr.setType(OpCode.error); request.txn = new ErrorTxn(e.code().intValue()); } LOG.info("Got user-level KeeperException when processing " + request.toString() + " Error Path:" + e.getPath() + " Error:" + e.getMessage()); request.setException(e); } 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; if(bb != null){ bb.rewind(); while (bb.hasRemaining()) { sb.append(Integer.toHexString(bb.get() & 0xff)); } } else { sb.append("request buffer is null"); } LOG.error("Dumping request buffer: 0x" + sb.toString()); if (request.hdr != null) { request.hdr.setType(OpCode.error); request.txn = new ErrorTxn(Code.MARSHALLINGERROR.intValue()); } } // 給請求的zxid賦值 request.zxid = zks.getZxid(); // 傳遞給下個處理器進行處理 nextProcessor.processRequest(request); }
說明:pRequest會確定請求型別,並根據請求型別不同生成不同的請求物件,然後呼叫pRequest2Txn函式,其原始碼如下
protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize) throws KeeperException, IOException, RequestProcessorException { // 新生事務頭 request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, zks.getTime(), type); switch (type) { // 確定型別 case OpCode.create: // 建立節點操作 // 檢查會話,檢查會話持有者是否為該owner zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 向下轉化 CreateRequest createRequest = (CreateRequest)record; if(deserialize) // 反序列化,將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, createRequest); // 獲取節點路徑 String path = createRequest.getPath(); // 索引最後一個'/' int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || failCreate) { // 判斷最後一個'/'是否合法 LOG.info("Invalid path " + path + " with session 0x" + Long.toHexString(request.sessionId)); throw new KeeperException.BadArgumentsException(path); } // 移除重複的ACL項 List<ACL> listACL = removeDuplicates(createRequest.getAcl()); if (!fixupACL(request.authInfo, listACL)) { // 確保ACL列表不為空 throw new KeeperException.InvalidACLException(path); } // 提取節點的父節點路徑 String parentPath = path.substring(0, lastSlash); // 獲取父節點的Record ChangeRecord parentRecord = getRecordForPath(parentPath); // 檢查ACL列表 checkACL(zks, parentRecord.acl, ZooDefs.Perms.CREATE, request.authInfo); // 獲取父節點的Record的子節點版本號 int parentCVersion = parentRecord.stat.getCversion(); // 獲取建立模式 CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags()); if (createMode.isSequential()) { // 順序模式 // 在路徑後新增一串數字 path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion); } try { // 驗證路徑 PathUtils.validatePath(path); } catch(IllegalArgumentException ie) { LOG.info("Invalid path " + path + " with session 0x" + Long.toHexString(request.sessionId)); throw new KeeperException.BadArgumentsException(path); } try { if (getRecordForPath(path) != null) { throw new KeeperException.NodeExistsException(path); } } catch (KeeperException.NoNodeException e) { // ignore this one } // 父節點是否為臨時節點 boolean ephemeralParent = parentRecord.stat.getEphemeralOwner() != 0; if (ephemeralParent) { // 父節點為臨時節點 throw new KeeperException.NoChildrenForEphemeralsException(path); } // 新的子節點版本號 int newCversion = parentRecord.stat.getCversion()+1; // 新生事務 request.txn = new CreateTxn(path, createRequest.getData(), listACL, createMode.isEphemeral(), newCversion); // StatPersisted s = new StatPersisted(); if (createMode.isEphemeral()) { // 建立節點為臨時節點 s.setEphemeralOwner(request.sessionId); } // 拷貝 parentRecord = parentRecord.duplicate(request.hdr.getZxid()); // 子節點數量加1 parentRecord.childCount++; // 設定新的子節點版本號 parentRecord.stat.setCversion(newCversion); // 將parentRecord新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(parentRecord); // 將新生成的ChangeRecord(包含了StatPersisted資訊)新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(new ChangeRecord(request.hdr.getZxid(), path, s, 0, listACL)); break; case OpCode.delete: // 刪除節點請求 // 檢查會話,檢查會話持有者是否為該owner zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 向下轉化為DeleteRequest DeleteRequest deleteRequest = (DeleteRequest)record; if(deserialize) // 反序列化,將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, deleteRequest); // 獲取節點路徑 path = deleteRequest.getPath(); // 索引最後一個'/' lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || zks.getZKDatabase().isSpecialPath(path)) { throw new KeeperException.BadArgumentsException(path); } // 提取節點的父節點路徑 parentPath = path.substring(0, lastSlash); // 獲取父節點的Record parentRecord = getRecordForPath(parentPath); // 獲取節點的Record ChangeRecord nodeRecord = getRecordForPath(path); // 檢查ACL列表 checkACL(zks, parentRecord.acl, ZooDefs.Perms.DELETE, request.authInfo); // 獲取版本 int version = deleteRequest.getVersion(); if (version != -1 && nodeRecord.stat.getVersion() != version) { throw new KeeperException.BadVersionException(path); } if (nodeRecord.childCount > 0) { // 該結點有子節點,丟擲異常 throw new KeeperException.NotEmptyException(path); } // 新生刪除事務 request.txn = new DeleteTxn(path); // 拷貝父節點Record parentRecord = parentRecord.duplicate(request.hdr.getZxid()); // 父節點的孩子節點數目減1 parentRecord.childCount--; // // 將parentRecord新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(parentRecord); // 將新生成的ChangeRecord(包含了StatPersisted資訊)新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(new ChangeRecord(request.hdr.getZxid(), path, null, -1, null)); break; case OpCode.setData: // 設定資料請求 // 檢查會話,檢查會話持有者是否為該owner zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 向下轉化 SetDataRequest setDataRequest = (SetDataRequest)record; if(deserialize) // 反序列化,將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest); // 獲取節點路徑 path = setDataRequest.getPath(); // 獲取節點的Record nodeRecord = getRecordForPath(path); // 檢查ACL列表 checkACL(zks, nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo); // 獲取請求的版本號 version = setDataRequest.getVersion(); // 節點當前版本號 int currentVersion = nodeRecord.stat.getVersion(); if (version != -1 && version != currentVersion) { throw new KeeperException.BadVersionException(path); } // 新生版本號 version = currentVersion + 1; // 新生設定資料事務 request.txn = new SetDataTxn(path, setDataRequest.getData(), version); // 拷貝 nodeRecord = nodeRecord.duplicate(request.hdr.getZxid()); // 設定版本號 nodeRecord.stat.setVersion(version); // 將nodeRecord新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(nodeRecord); break; case OpCode.setACL: // 設定ACL請求 // 檢查會話,檢查會話持有者是否為該owner zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 向下轉化 SetACLRequest setAclRequest = (SetACLRequest)record; if(deserialize) // 反序列化,將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, setAclRequest); // 獲取節點路徑 path = setAclRequest.getPath(); // 移除重複的ACL項 listACL = removeDuplicates(setAclRequest.getAcl()); if (!fixupACL(request.authInfo, listACL)) { // 確保ACL列表不為空 throw new KeeperException.InvalidACLException(path); } // 獲取節點的Record nodeRecord = getRecordForPath(path); // 檢查ACL列表 checkACL(zks, nodeRecord.acl, ZooDefs.Perms.ADMIN, request.authInfo); // 獲取版本號 version = setAclRequest.getVersion(); // 當前版本號 currentVersion = nodeRecord.stat.getAversion(); if (version != -1 && version != currentVersion) { // 驗證版本號 throw new KeeperException.BadVersionException(path); } // 新生版本號 version = currentVersion + 1; // 設定請求事務 request.txn = new SetACLTxn(path, listACL, version); // 拷貝 nodeRecord = nodeRecord.duplicate(request.hdr.getZxid()); // 設定ACL版本號 nodeRecord.stat.setAversion(version); // 將nodeRecord新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(nodeRecord); break; case OpCode.createSession: // 建立會話請求 // 將request緩衝區rewind request.request.rewind(); // 獲取緩衝區大小 int to = request.request.getInt(); // 建立會話事務 request.txn = new CreateSessionTxn(to); // 再次將request緩衝區rewind request.request.rewind(); // 新增session zks.sessionTracker.addSession(request.sessionId, to); // 設定會話的owner zks.setOwner(request.sessionId, request.getOwner()); break; case OpCode.closeSession: // 關閉會話請求 // We don't want to do this check since the session expiration thread // queues up this operation without being the session owner. // this request is the last of the session so it should be ok //zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 獲取會話所有的臨時節點 HashSet<String> es = zks.getZKDatabase() .getEphemerals(request.sessionId); synchronized (zks.outstandingChanges) { for (ChangeRecord c : zks.outstandingChanges) { // 遍歷outstandingChanges佇列的所有ChangeRecord if (c.stat == null) { // 若其stat為null // Doing a delete // 則從es中移除其路徑 es.remove(c.path); } else if (c.stat.getEphemeralOwner() == request.sessionId) { // 若臨時節點屬於該會話 // 則將其路徑新增至es中 es.add(c.path); } } for (String path2Delete : es) { // 遍歷es // 新生ChangeRecord,並將其新增至outstandingChanges和outstandingChangesForPath中 addChangeRecord(new ChangeRecord(request.hdr.getZxid(), path2Delete, null, 0, null)); } // 關閉會話 zks.sessionTracker.setSessionClosing(request.sessionId); } LOG.info("Processed session termination for sessionid: 0x" + Long.toHexString(request.sessionId)); break; case OpCode.check: // 檢查請求 // 檢查會話,檢查會話持有者是否為該owner zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); // 向下轉化 CheckVersionRequest checkVersionRequest = (CheckVersionRequest)record; if(deserialize) // 反序列化,將ByteBuffer轉化為Record ByteBufferInputStream.byteBuffer2Record(request.request, checkVersionRequest); // 獲取節點路徑 path = checkVersionRequest.getPath(); // 獲取節點的Record nodeRecord = getRecordForPath(path); // 檢查ACL列表 checkACL(zks, nodeRecord.acl, ZooDefs.Perms.READ, request.authInfo); // 獲取版本號 version = checkVersionRequest.getVersion(); // 當前版本號 currentVersion = nodeRecord.stat.getVersion(); if (version != -1 && version != currentVersion) { // 驗證版本號 throw new KeeperException.BadVersionException(path); } // 新生版本號 version = currentVersion + 1; // 新生請求的事務 request.txn = new CheckVersionTxn(path, version); break; } }
說明:pRequest2Txn會根據不同的請求型別進行不同的驗證,如對建立節點而言,其會進行會話驗證,ACL列表驗證,節點路徑驗證及判斷建立節點的型別(順序節點、臨時節點等)而進行不同操作,同時還會使父節點的子節點數目加1,之後會再呼叫addChangeRecord函式將ChangeRecord新增至ZooKeeperServer的outstandingChanges和outstandingChangesForPath中。
在pRequest函式中,如果請求型別是多重操作,那麼會呼叫getPendingChanges函式,其會獲取掛起的更改,其原始碼如下
HashMap<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { HashMap<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for(Op op: multiRequest) { // String path = op.getPath(); try { // 獲取path對應的ChangeRecord ChangeRecord cr = getRecordForPath(path); if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } // 提取節點的父節點路徑 String parentPath = path.substring(0, lastSlash); // 獲取父節點的Record ChangeRecord parentCr = getRecordForPath(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } catch (KeeperException.NoNodeException e) { // ignore this one } } return pendingChangeRecords; }
說明:可以看到在函式中,會遍歷多重操作,針對每個操作,通過其路徑獲取對應的Record,然後新增至pendingChangeRecords,然後對其父節點進行相應操作,之後返回,其中會呼叫getRecordForPath函式,其原始碼如下
ChangeRecord getRecordForPath(String path) throws KeeperException.NoNodeException { ChangeRecord lastChange = null; synchronized (zks.outstandingChanges) { // 同步塊 // 先從outstandingChangesForPath佇列中獲取 lastChange = zks.outstandingChangesForPath.get(path); /* for (int i = 0; i < zks.outstandingChanges.size(); i++) { ChangeRecord c = zks.outstandingChanges.get(i); if (c.path.equals(path)) { lastChange = c; } } */ if (lastChange == null) { // 若在outstandingChangesForPath中未獲取到,則從資料庫中獲取 DataNode n = zks.getZKDatabase().getNode(path); if (n != null) { // 節點存在 Long acl; Set<String> children; synchronized(n) { acl = n.acl; children = n.getChildren(); } // 新生ChangeRecord lastChange = new ChangeRecord(-1, path, n.stat, children != null ? children.size() : 0, zks.getZKDatabase().convertLong(acl)); } } } if (lastChange == null || lastChange.stat == null) { // 丟擲異常 throw new KeeperException.NoNodeException(path); } return lastChange; }
說明:其表示根據節點路徑獲取節點的Record,其首先會從outstandingChangesForPath中獲取路徑對應的Record,若未獲取成功,則從Zookeeper資料庫中獲取,若還未存在,則丟擲異常。
2. processResult函式
public void processRequest(Request request) { // request.addRQRec(">prep="+zks.outstandingChanges.size()); // 將請求新增至佇列中 submittedRequests.add(request); }
說明:該函式是對父介面函式的實現,其主要作用是將請求新增至submittedRequests佇列進行後續處理(run函式中)。
三、總結
針對PrepRequestProcessor的原始碼就分析到這裡,其完成的業務邏輯也相對簡單,其通常是處理鏈的第一個處理器,也謝謝各位園友的觀看~