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

leesf發表於2017-02-27

一、前言

  前面分析了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);
        }
    }
View Code

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

相關文章