1. 程式人生 > 其它 >tomcat原始碼解讀五 Tomcat中Request的生命歷程

tomcat原始碼解讀五 Tomcat中Request的生命歷程

     Request在tomcat中是一個非常核心的的例項,下面以NIO為例來解讀一下在各個時期下的狀態(其實在Tomcat的幾種模式中到了這裡之後的處理都是差不多的)

1.1 建立coyote/Request

     這個request並不是我們最終在servlet中使用的Request,它是tomcat內部處理請求的一種有效方法,其建立過程是在接收到客戶請求處理套接字構建Processor具體實現類的構造器中構建,以NIO模式為例則是在例項化請求處理類Http11NioProcessor時候構建,具體執行流程如下:

Http11NioProcessor.java

public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
        Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {

    super(endpoint);

    inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize);
    request.setInputBuffer(inputBuffer);

    outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
    response.setOutputBuffer(outputBuffer);

    initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
}

AbstractHttp11Processor.java

public AbstractHttp11Processor(AbstractEndpoint<S> endpoint) {
    super(endpoint);
    userDataHelper = new UserDataHelper(getLog());
}

AbstractProcessor.java

public AbstractProcessor(AbstractEndpoint<S> endpoint) {
    this.endpoint = endpoint;
    asyncStateMachine = new AsyncStateMachine(this);
    request = new Request();
    response = new Response();
    response.setHook(this);
    request.setResponse(response);
    request.setHook(this);
}

     根據這個過程不難看出在例項化中逐級顯示呼叫父級有參構造器,將對應的endpoint賦給Processor實現類的控制代碼,而後繼續例項化Request,並將當前例項注入到新構建的request例項,另外response也被注入request作為控制代碼。

1.2 Coyote/Request的執行與結束      Coyote/Request的執行與結束主要是在Processor. process在這個過程中會獲取RequestInfo這個控制代碼其是一個request初始化例項控制代碼,在這個方法中通過setRequestLineReadTimeout方法解析了請求行如Method URL HTTP/1.1利用parseRequestLine剩餘的首部資訊最終呼叫request中相關的方法將解析的資訊(大部分是MessageByte)注入到其成員屬性中(詳見requets解析http頭部請求),然後進行的是呼叫Adapter的service方法進行處理(見下一小節),之後利用endRequest將當前請求處理完成,在這個過程還包含將流給提交http中去,最後呼叫nextRequest將當前request例項部分屬性置空,所以當前例項依舊存在。

public SocketState process(SocketWrapper<S> socketWrapper)
    throws IOException {
    //獲取請求資訊控制代碼,其在定義的時候直接初始化的一個RequestInfo例項
    RequestInfo rp = request.getRequestProcessor();
    //設定RequestInfo的狀態為解析狀態  stage_parse
    rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
    。。。。。。

    while (!getErrorState().isError() && keepAlive && !comet && !isAsync() && upgradeToken == null && !endpoint.isPaused()) {
            。。。。。。   
            //設定請求閱讀時間,以及將socketInputStream中的資料寫入到buf
            setRequestLineReadTimeout();
            //if判斷裡只是解析了第一行,方法名 請求URL 協議
            if (!getInputBuffer().parseRequestLine(keptAlive)) {
                //如果解析失敗,處理未完成的請
                if (handleIncompleteRequestLineRead()) {
                    break;
                }
。。。。。。。。
        }
        if (!getErrorState().isError()) {
            //設定過濾器準備解析
            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
            try {
    //解析請求,根據url將對應的Host Context Wrapper匹配到該請request的屬性中
                prepareRequest();
            } catch (Throwable t) {
               。。。。。。。。            
}
        }
        //最大的長連線數
        if (maxKeepAliveRequests == 1) {
            keepAlive = false;
        } else if (maxKeepAliveRequests > 0 &&
                socketWrapper.decrementKeepAlive() <= 0) {
            keepAlive = false;
        }
        // 在介面卡中執行請求
        if (!getErrorState().isError()) {
                //設定request的狀態為開始呼叫介面卡的service
            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                //呼叫介面卡的service方法
                getAdapter().service(request, response);
          。。。。。。。
        }

           rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

        if (!isAsync() && !comet) {
            。。。。。。
            endRequest();
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
        if (!isAsync() && !comet || getErrorState().isError()) {
            request.updateCounters();
            if (getErrorState().isIoAllowed()) {
                getInputBuffer().nextRequest();
                getOutputBuffer().nextRequest();
            }
        }

        。。。。。。

        rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

   }

1.3 建立Coonnector/Request和Connector/Response

     這兩個例項和是從相應的Coyote對應例項的Notes 陣列中獲取的,如果沒有則例項化一個並且注入,這是因為Coyote和Coonnector中相關例項是一一對應,只不過Coyote主要是負責和http打交道而Coonnector是和程式設計師打交道,但是請注意我們並不是直接使用的Coonnector中Request/Response。後面講述

//從org.apache.coyote.Request的note陣列屬性中獲取Request物件
Request request = (Request) req.getNote(ADAPTER_NOTES);
//從org.apache.coyote.Response的note陣列屬性中獲取Response物件
Response response = (Response) res.getNote(ADAPTER_NOTES);
//解析:ADAPTER_NOTES=1 這是因為notes這個陣列不知存放了相應request/Response例項 還有cookie等 1代表的是Request/Response
if (request == null) {
    //建立一個connector的request物件
    request = connector.createRequest();
    //將Coyote中request注入聯結器中
    request.setCoyoteRequest(req);
    response = connector.createResponse();
    response.setCoyoteResponse(res);
    //request response相互關聯
    request.setResponse(response);
    response.setRequest(request);
    //設定為notes
    req.setNote(ADAPTER_NOTES, request);
    res.setNote(ADAPTER_NOTES, response);
    req.getParameters().setQueryStringEncoding
        (connector.getURIEncoding());

}

if (connector.getXpoweredBy()) {
    response.addHeader("X-Powered-By", POWERED_BY);
}

1.4 Coonnector/Request和Connector/Response的執行過程

     其建立之後就直接通過獲取service不斷呼叫管道一下向下執行找到對應的servlet進行執行,其過程如下:

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

     在這個過程中,針對Request/Response的生命歷程,我們應該提的是StandardWrapperValve這個閥門執行的程式碼是如下:      filterChain.doFilter(request.getRequest(),response.getResponse())

public HttpServletRequest getRequest() {
    if (facade == null) {
        facade = new RequestFacade(this);
    }
    return facade;
}

     可以看出在這裡採用了外觀模式建立了RequestFacade例項,並作為引數不斷向下傳遞,這就是我們在servlet中使用的RequestFacade這個例項在servlet執行完畢,接著管道繼續向下執到finishRequest, finishResponse完成當前請求,其中finishResponse是將最終的相應資料給傳送到客戶端

1.5 Coonnector/Request和Connector/Response的結束處理 Request/Response也不是直接從記憶體釋放,僅僅只是其中部分屬性給置空,下一個socket請求的時候呼叫的是對應的Processor具體實現類則可以直接進行獲取。置空程式碼如下:

if (!comet && !async) {
    request.recycle();
    response.recycle();
} else {
    request.clearEncoders();
    response.clearEncoders();
}

由於Request請求跟瀏覽器無關,可能多次請求是一個Request例項,也可能是不同例項,但是在請求中Request例項中對應的成員屬性都被清空,所以可以說Request的作用域是單個請求,Response也是同樣的道理