目標
分析tomcat處理一次http請求的nio過程模式,因為bio比較常見诅诱,網(wǎng)上好多分析資料可參考千贯。
分析
上一節(jié)屯仗,我們已經(jīng)分析了tomcat端啟動流程,最后Connector中啟動Endpoint進行端口監(jiān)聽搔谴,然后在mapperListener中存了相關各容器的映射關系魁袜,最后pipeline作為容器的調(diào)用管道鏈。按照這個流程敦第,我們分析調(diào)用源碼峰弹,進行debug,看下最終我的nio調(diào)用時序圖:
簡單語言描述下:
- 請求進入NioEndPoint的內(nèi)部Acceptor接收請求芜果,根據(jù)nio的異步處理機制鞠呈,socket會作為一個pollerEvent事件存入隊列,poller會輪詢通過select進行選擇可讀事件
protected class Acceptor implements Runnable {
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
右钾。蚁吝。旱爆。。灭将。疼鸟。。庙曙。
Thread.sleep(1000);
。浩淘。捌朴。。张抄。砂蔽。。
//每次用完一個新建
SocketChannel socket = null;
socket = serverSock.accept();
..............
//此處去注冊任務了,然后關閉此socket
if (!setSocketOptions(socket)) {
try {
socket.socket().close();
socket.close();
署惯。左驾。。极谊。诡右。。轻猖。帆吻。。咙边。猜煮。。败许。王带。
}//run
}
對應的setSocketOptions方法
protected boolean setSocketOptions(SocketChannel socket) {
//獲取NioChannel,沒有空閑的則新建一個
NioChannel channel = nioChannels.poll();
市殷。愕撰。。被丧。盟戏。。甥桂。柿究。。黄选。
//注冊poller事件任務
getPoller0().register(channel);
蝇摸。婶肩。。貌夕。律歼。。啡专。
return true;
}
- selector找到待處理事件后開啟異步線程調(diào)用SocketProcessor進行處理险毁,SocketProcessor找到其對應的協(xié)議處理類封裝request,最后通過CoyoteAdapter進行適配處理
看下Poller中監(jiān)聽run代碼
public void run() {
// Loop until we receive a shutdown command
while (running) {
try {
// Loop if endpoint is paused
while (paused && (!close) ) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
}
//判斷是否有待處理任務
boolean hasEvents = events();
//通過nio的selector選擇可讀事件们童,進行處理
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
//處理此key
processKey(sk, attachment);
}
然后進入processKey處理數(shù)據(jù),此方法最終調(diào)用processSocket(channel, null, true)方法
public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
try {
KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
attachment.setCometNotify(false); //will get reset upon next reg
SocketProcessor sc = processorCache.poll();
if ( sc == null ) sc = new SocketProcessor(socket,status);
else sc.reset(socket,status);
//獲取SocketProcessor處理器畔况,如果配置了getExecutor則在異步線程中進行處理,否則直接處理
if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc);
else sc.run();
} catch (RejectedExecutionException rx) {
log.warn("Socket processing request was rejected for:"+socket,rx);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
在SocketProcessor的run方法中慧库,核心部分我們看到
state = (status==null)?handler.process(socket):handler.event(socket,status);
這個handler為Http11NioProtocol跷跪,根據(jù)我們配置的協(xié)議形成,看下其process方法,最終找到其對應方法SocketState state = processor.process(socket);
,此時processor為協(xié)議對應的處理器Http11NioProcessor,簡化此方法
public SocketState process(NioChannel socket)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
齐板。吵瞻。。甘磨。橡羞。。宽档。尉姨。
//處理request數(shù)據(jù),session吗冤,cookie等信息都在此時進行處理
prepareRequest();
//適配找到對應容器業(yè)務
adapter.service(request, response);
又厉。。椎瘟。覆致。。肺蔚。
}
}
- CoyoteAdapter首先會解析request煌妈,最后調(diào)用pipeline調(diào)用鏈進入container
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
//處理request和response信息
.............
//此處會調(diào)用pipeline逐級調(diào)用進入engine、host宣羊、context璧诵、wrapper connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
...............
//成功后,返回信息
response.finishResponse();
if (postParseSuccess) {
// Log only if processing was invoked.
// If postParseRequest() failed, it has already logged it.
((Context) request.getMappingData().context).logAccess(
request, response,
System.currentTimeMillis() - req.getStartTime(),
false);
}
req.action(ActionCode.POST_REQUEST , null);
}
仇冯。之宿。。苛坚。比被。色难。。等缀。枷莉。
}
}
- 在pipeline層層傳遞下最后進入StandardWrapperValve找到最終的servlet,匹配path對應的filter尺迂,包裝filer鏈笤妙,調(diào)用filter的doFilter方法,最后進入servlet執(zhí)行業(yè)務
public final void invoke(Request request, Response response)
throws IOException, ServletException {
噪裕。危喉。。州疾。。
//找到對應的servlet
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
皇拣。严蓖。。氧急。颗胡。。吩坝。毒姨。。
//形成過濾器調(diào)用鏈
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
钉寝。弧呐。。嵌纲。俘枫。。逮走。
}
- servlet執(zhí)行完后鸠蚪,CoyoteAdapter會調(diào)用finishResponse方法關閉輸出流,返回客戶端师溅。
知識點:
tomcat中nio的實現(xiàn)茅信,加深對nio概念理解
適配器模式進行接口適配
3.pipeline和filter等責任鏈模式的使用
參考資料:https://blog.csdn.net/sunyunjie361/article/details/60126398
目錄:?tomcat 源碼學習系列
上一篇: ? tomcat啟動源碼分析(二)--入口代碼calatina啟動介紹
下一篇:? tomcat啟動源碼分析(三)--http請求nio處理