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也是同樣的道理