花了一個(gè)禮拜的時(shí)間閱讀了how tomcat works,本文基于此書,整理了一下Tomcat 5的基本架構(gòu),其實(shí)也沒什么多復(fù)雜的東西,無(wú)非是解析Http請(qǐng)求,然后調(diào)用相應(yīng)的Servlet。另推薦看CSAPP的網(wǎng)絡(luò)編程那一章
Tomcat由兩個(gè)模塊協(xié)同合作
connector containerconnector負(fù)責(zé)解析處理HTTP請(qǐng)求,比如說(shuō)請(qǐng)求頭,查詢字符串,請(qǐng)求參數(shù)之類的。生成HttpRequest和HttpResponse
之后交給container,由它負(fù)責(zé)調(diào)用相應(yīng)的Servlet。
Tomcat默認(rèn)的Connector為HttpConnector。作為Connector必須要實(shí)現(xiàn)Connector這個(gè)接口。
Tomcat啟動(dòng)以后會(huì)開啟一個(gè)線程,做一個(gè)死循環(huán),通過(guò)ServerSocket來(lái)等待請(qǐng)求。一旦得到請(qǐng)求,生成Socket,注意這里HttpConnector并不會(huì)自己處理Socket,而是把它交給HttpProcessor。詳細(xì)看下面代碼,這里我只保留了關(guān)鍵代碼。
public void run() { // Loop until we receive a shutdown command while (!stopped) { Socket socket = null; try { socket = serverSocket.accept(); //等待鏈接 } catch (AccessControlException ace) { log("socket accept security exception", ace); continue; } // Hand this socket off to an appropriate processor HttpProcessor processor = createProcessor(); processor.assign(socket); //這里是立刻返回的 // The processor will recycle itself when it finishes } }
注意一點(diǎn),上面的processor.assign(socket);是立刻返回的,并不會(huì)阻塞在那里等待。因?yàn)門omcat不可能一次只能處理一個(gè)請(qǐng)求,所以是異步的,每個(gè)processor處理都是一個(gè)單獨(dú)的線程。
HttpProcessor上面的代碼并沒有顯示調(diào)用HttpProcessor的process方法,那這個(gè)方法是怎么調(diào)用的呢?我們來(lái)看一下HttpProcessor的run方法。
public void run() { // Process requests until we receive a shutdown signal while (!stopped) { // Wait for the next socket to be assigned Socket socket = await(); if (socket == null) continue; // Process the request from this socket try { process(socket); } catch (Throwable t) { log("process.invoke", t); } // Finish up this request connector.recycle(this); } }
我們發(fā)現(xiàn)他是調(diào)用await方法來(lái)阻塞等待獲得socket方法。而之前Connector是調(diào)用assign分配的,這是什么原因?
下面仔細(xì)看await和assign方法。這兩個(gè)方法協(xié)同合作,當(dāng)assign獲取socket時(shí)會(huì)通知await然后返回socket。
synchronized void assign(Socket socket) { // Wait for the Processor to get the previous Socket while (available) { try { wait(); } catch (InterruptedException e) { } } // Store the newly available Socket and notify our thread this.socket = socket; available = true; notifyAll(); } private synchronized Socket await() { // Wait for the Connector to provide a new Socket while (!available) { try { wait(); } catch (InterruptedException e) { } } // Notify the Connector that we have received this Socket Socket socket = this.socket; available = false; notifyAll(); return (socket); }
默認(rèn)available為false。
接下來(lái)就是剩下的事情就是解析請(qǐng)求,填充HttpRequest和HttpResponse對(duì)象,然后交給container負(fù)責(zé)。
這里我不過(guò)多贅述如何解析。
private void process(Socket socket) { //parse .... connector.getContainer().invoke(request, response); .... } Container
A Container is an object that can execute requests received from a client, and return responses based on those requests
Container是一個(gè)接口,實(shí)現(xiàn)了這個(gè)接口的類的實(shí)例,可以處理接收的請(qǐng)求,調(diào)用對(duì)應(yīng)的Servlet。
總共有四類Container,這四個(gè)Container之間并不是平行關(guān)系,而是父子關(guān)系
Engine- 最頂層的容器,可以包含多個(gè)Host Host- 代表一個(gè)虛擬主機(jī),可以包含多個(gè)Context Context- 代表一個(gè)web應(yīng)用,也就是ServletContext,可以包含多個(gè)Wrappers Wrapper- 代表一個(gè)Servlet,不能包含別的容器了,這是最底層 Container的調(diào)用容器好比是一個(gè)加工廠,加工接受的request,加工方式和流水線也很像,但又有點(diǎn)區(qū)別。這里會(huì)用到一個(gè)叫做Pipeline的 東西,中文翻譯為管道,request就放在管道里順序加工,進(jìn)行加工的工具叫做Valve,好比手術(shù)刀,Pipeline可添加多個(gè)Valve,最后加工的工具稱為BaseValve
上面可能講的比較抽象,接下來(lái)我們來(lái)看代碼。Engine是頂層容器,所以上面invoke,執(zhí)行的就是Engine的方法。StandardEngine是Engine的默認(rèn)實(shí)現(xiàn),注意它也同時(shí)實(shí)現(xiàn)了Pipeline接口,且包含了Pipeline。
它的構(gòu)造方法同時(shí)指定了baseValve,也就是管道最后一個(gè)調(diào)用的Valve
public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); }
好,接著我們看invoke,這個(gè)方法是繼承自ContainerBase。只有一行,之間交給pipeline,進(jìn)行加工。
public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); }
下面是StandardPipeline的invoke實(shí)現(xiàn),也就是默認(rèn)的pipeline實(shí)現(xiàn)。
public void invoke(Request request, Response response) throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request (new StandardPipelineValveContext()).invokeNext(request, response); }
也只有一行!調(diào)用StandardPipelineValveContext的invokeNext方法,這是一個(gè)pipeline的內(nèi)部類。讓我們來(lái)看
具體代碼
public void invokeNext(Request request, Response response) throws IOException, ServletException { int subscript = stage; stage = stage + 1; // Invoke the requested Valve for the current request thread if (subscript < valves.length) { valves[subscript].invoke(request, response, this); //加工 } else if ((subscript == valves.length) && (basic != null)) { basic.invoke(request, response, this); } else { throw new ServletException (sm.getString("standardPipeline.noValve")); } }
它調(diào)用了pipeline所用的Valve來(lái)對(duì)request做加工,當(dāng)Valve執(zhí)行完,會(huì)調(diào)用BaseValve,也就是上面的StandardEngineValve,
我們?cè)賮?lái)看看它的invoke方法:
// Select the Host to be used for this Request StandardEngine engine = (StandardEngine) getContainer(); Host host = (Host) engine.map(request, true); if (host == null) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost", request.getRequest().getServerName())); return; } // Ask this Host to process this request host.invoke(request, response);
它通過(guò)(Host) engine.map(request, true);獲取所對(duì)應(yīng)的Host,然后進(jìn)入到下一層容器中繼續(xù)執(zhí)行。后面的執(zhí)行順序
和Engine相同,我不過(guò)多贅述
經(jīng)過(guò)一長(zhǎng)串的invoke終于講完了第一層容器的執(zhí)行順序。估計(jì)你們看的有點(diǎn)暈,我這里小結(jié)一下。
Connector -> HttpProcessor.process() -> StandardEngine.invoke() -> StandardPipeline.invoke() ->
StandardPipelineValveContext.invokeNext() -> valves.invoke() -> StandardEngineValve.invoke() ->
StandardHost.invoke()
到這里位置Engine這一層結(jié)束。接下來(lái)進(jìn)行Host,步驟完全一致
StandardHost.invoke() -> StandardPipeline.invoke() ->
StandardPipelineValveContext.invokeNext() -> valves.invoke() -> StandardHostValve.invoke() ->
StandardContext.invoke()
然后再進(jìn)行Context這一層的處理,到最后選擇對(duì)應(yīng)的Wrapping執(zhí)行。
WrapperWrapper相當(dāng)于一個(gè)Servlet實(shí)例,StandardContext會(huì)更根據(jù)的request來(lái)選擇對(duì)應(yīng)的Wrapper調(diào)用。我們直接來(lái)看看
Wrapper的basevalve是如果調(diào)用Servlet的service方法的。下面是StandardWrapperValve的invoke方法,我省略了很多,
只看關(guān)鍵。
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Allocate a servlet instance to process this request if (!unavailable) { servlet = wrapper.allocate(); } // Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet\'s service() method String jspFile = wrapper.getJspFile(); //是否是jsp if (jspFile != null) sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile); else sreq.removeAttribute(Globals.JSP_FILE_ATTR); if ((servlet != null) && (filterChain != null)) { filterChain.doFilter(sreq, sres); } sreq.removeAttribute(Globals.JSP_FILE_ATTR); }
首先調(diào)用wrapper.allocate(),這個(gè)方法很關(guān)鍵,它會(huì)通過(guò)反射找到對(duì)應(yīng)servlet的class文件,構(gòu)造出實(shí)例返回給我們。然后創(chuàng)建一個(gè)FilterChain,熟悉j2ee的各位應(yīng)該對(duì)這個(gè)不陌生把?這就是我們?cè)陂_發(fā)web app時(shí)使用的filter。然后就執(zhí)行doFilter方法了,它又會(huì)調(diào)用internalDoFilter,我們來(lái)看這個(gè)方法
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (this.iterator.hasNext()) { ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) iterator.next(); Filter filter = null; filter = filterConfig.getFilter(); filter.doFilter(request, response, this); return; } // We fell off the end of the chain -- call the servlet instance if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { servlet.service((HttpServletRequest) request, (HttpServletResponse) response); } else { servlet.service(request, response); } }
終于,在這個(gè)方法里看到了service方法,現(xiàn)在你知道在使用filter的時(shí)候如果不執(zhí)行doFilter,service就不會(huì)執(zhí)行的原因了把。
小結(jié)Tomcat的重要過(guò)程應(yīng)該都在這里了,還值得一提的是LifeCycle接口,這里所有類幾乎都實(shí)現(xiàn)了LifeCycle,Tomcat通過(guò)它來(lái)統(tǒng)一管理容器的生命流程,大量運(yùn)用觀察者模式。有興趣的同學(xué)可以自己看書
ReferanceHow Tomcat works
文章名稱:Tomcat架構(gòu)探索
網(wǎng)頁(yè)URL:http://www.rwnh.cn/article36/cgpssg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、網(wǎng)站排名、關(guān)鍵詞優(yōu)化、面包屑導(dǎo)航、網(wǎng)站內(nèi)鏈、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)