Tomcat處理http請求之源碼分析
1 請求包裝處理
tomcat組件Connector在啟動的時候會監(jiān)聽端口。以JIoEndpoint為例,在其Acceptor類中:
protected class Acceptor extends AbstractEndpoint.Acceptor { @Override public void run() { while (running) { …… try { //當(dāng)前連接數(shù) countUpOrAwaitConnection(); Socket socket = null; try { //取出隊列中的連接請求 socket = serverSocketFactory.acceptSocket(serverSocket); } catch (IOException ioe) { countDownConnection(); } if (running && !paused && setSocketOptions(socket)) { //處理請求 if (!processSocket(socket)) { countDownConnection(); closeSocket(socket); } } else { countDownConnection(); // Close socket right away closeSocket(socket); } } …… } } }
在上面的代碼中,socket = serverSocketFactory.acceptSocket(serverSocket);與客戶端建立連接,將連接的socket交給processSocket(socket)來處理。在processSocket中,對socket進(jìn)行包裝一下交給線程池來處理:
protected boolean processSocket(Socket socket) { try { SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket); wrapper.setKeepAliveLeft(getMaxKeepAliveRequests()); wrapper.setSecure(isSSLEnabled()); //交給線程池處理連接 getExecutor().execute(new SocketProcessor(wrapper)); } …… return true; }
線程池處理的任務(wù)SocketProccessor,通過代碼分析:
protected class SocketProcessor implements Runnable { protected SocketWrapper<Socket> socket = null; protected SocketStatus status = null; @Override public void run() { boolean launch = false; synchronized (socket) { SocketState state = SocketState.OPEN; try { serverSocketFactory.handshake(socket.getSocket()); } …… if ((state != SocketState.CLOSED)) { //委派給Handler來處理 if (status == null) { state = handler.process(socket, SocketStatus.OPEN_READ); } else { state = handler.process(socket,status); } }}} …… }
即在SocketProcessor中,將Socket交給handler處理,這個handler就是在Http11Protocol的構(gòu)造方法中賦值的Http11ConnectionHandler,在該類的父類process方法中通過請求的狀態(tài),來創(chuàng)建Http11Processor處理器進(jìn)行相應(yīng)的處理,切到Http11Proccessor的父類AbstractHttp11Proccessor中。
public SocketState process(SocketWrapper socketWrapper) { RequestInfo rp = request.getRequestProcessor(); rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // Setting up the I/O setSocketWrapper(socketWrapper); getInputBuffer().init(socketWrapper, endpoint); getOutputBuffer().init(socketWrapper, endpoint); while (!getErrorState().isError() && keepAlive && !comet && !isAsync() && upgradeInbound == null && httpUpgradeHandler == null && !endpoint.isPaused()) { …… if (!getErrorState().isError()) { // Setting up filters, and parse some request headers rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE); try { //請求預(yù)處理 prepareRequest(); } …… } …… if (!getErrorState().isError()) { try { rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE); //交由適配器處理 adapter.service(request, response); if(keepAlive && !getErrorState().isError() && ( response.getErrorException() != null || (!isAsync() && statusDropsConnection(response.getStatus())))) { setErrorState(ErrorState.CLOSE_CLEAN, null); } setCometTimeouts(socketWrapper); } } } …… }
可以看到Request和Response的生成,從Socket中獲取請求數(shù)據(jù),keep-alive處理,數(shù)據(jù)包裝等等信息,最后交給了CoyoteAdapter的service方法
2 請求傳遞給Container
在CoyoteAdapter的service方法中,主要有2個任務(wù):
•第一個是org.apache.coyote.Request和
org.apache.coyote.Response到繼承自HttpServletRequest的org.apache.catalina.connector.Request和org.apache.catalina.connector.Response轉(zhuǎn)換,和Context,Wrapper定位。
•第二個是將請求交給StandardEngineValve處理。
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) { …… postParseSuccess = postParseRequest(req, request, res, response); …… connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); …… }
在postParseRequest方法中代碼片段:
connector.getMapper().map(serverName, decodedURI, version, request.getMappingData()); request.setContext((Context) request.getMappingData().context); request.setWrapper((Wrapper) request.getMappingData().wrapper);
request通過URI的信息找到屬于自己的Context和Wrapper。而這個Mapper保存了所有的容器信息,不記得的同學(xué)可以回到Connector的startInternal方法中,最有一行代碼是mapperListener.start(); 在MapperListener的start()方法中,
public void startInternal() throws LifecycleException { setState(LifecycleState.STARTING); findDefaultHost(); Engine engine = (Engine) connector.getService().getContainer(); addListeners(engine); Container[] conHosts = engine.findChildren(); for (Container conHost : conHosts) { Host host = (Host) conHost; if (!LifecycleState.NEW.equals(host.getState())) { registerHost(host); } } }
MapperListener.startInternal()方法將所有Container容器信息保存到了mapper中。那么,現(xiàn)在初始化把所有容器都添加進(jìn)去了,如果容器變化了將會怎么樣?這就是上面所說的監(jiān)聽器的作用,容器變化了,MapperListener作為監(jiān)聽者。他的生成圖示
通過Mapper找到了該請求對應(yīng)的Context和Wrapper后,CoyoteAdapter將包裝好的請求交給Container處理。
3 Container處理請求流程
從下面的代碼片段,我們很容易追蹤整個Container的調(diào)用鏈: 用時序圖畫出來則是:
最終StandardWrapperValve將請求交給Servlet處理完成。至此一次http請求處理完畢。
到此這篇關(guān)于Tomcat處理http請求之源碼分析的文章就介紹到這了,更多相關(guān)Tomcat請求處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何在同一局域網(wǎng)下通過IP地址訪問本機Tomcat項目
這篇文章主要介紹了如何在同一局域網(wǎng)下通過IP地址訪問本機Tomcat項目問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06tomcat訪問(access)日志配置和記錄Post請求參數(shù)
這篇文章主要介紹了tomcat訪問(access)日志配置和記錄Post請求參數(shù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03tomcat的webapps目錄下的應(yīng)用刪除部署詳解
這篇文章主要介紹了tomcat的webapps目錄下的應(yīng)用刪除部署詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Tomcat9安裝windows服務(wù)的詳細(xì)教程
這篇文章主要介紹了Tomcat9安裝windows服務(wù)的教程,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11Linux下定時切割Tomcat日志并刪除指定天數(shù)前的日志記錄
這篇文章主要介紹了Linux下定時切割Tomcat日志并刪除指定天數(shù)前的日志記錄,需要的朋友可以參考下2017-08-08