【Zookeeper】原始碼分析之請求處理鏈(二)之PrepRequestProcessor

leesf發表於2017-02-20

一、前言

  前面學習了請求處理鏈的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);
    }
View Code

  說明: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;
        }
    }
View Code

  說明: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的原始碼就分析到這裡,其完成的業務邏輯也相對簡單,其通常是處理鏈的第一個處理器,也謝謝各位園友的觀看~

相關文章