大致架構java
Tomcat由一系列邏輯模塊組織而成,這些模塊主要包括: web
Service中配置了實際工做的Engine,同時配置了用來處理時間業務的線程組Executor(若是沒有配置則用系統默認的WorkThread模式的線程組),以及處理網絡socket的相關組件connector。apache
1) Executor是線程池,它的具體實現是java的concurrent包實現的executor,這個不是必須的,若是沒有配置,則使用自寫的worker thread線程池
2) Connector是網絡socket相關接口模塊,它包含兩個對象,ProtocolHandler及Adapter 數組
容器類:tomcat提供四種容器,繼承自同一個容器基類,有標準實現,也能夠定製化。tomcat
配置例子安全
<Engine name="Catalina" defaultHost="localhost"> <Valve className="MyValve0"/> <Valve className="MyValve1"/> <Valve className="MyValve2"/> …… <Host name="localhost" appBase="webapps"> </Host> </Engine>
運行環境中,pipeline上的valve數組按照配置的順序加載,可是不管有無配置定製化的valve或有多少定製化的valve,每一個容器缺省的valve,例如engine的StandardEngineValve,都會在數組中最後一個。 服務器
StandardHost的核心模塊與StandardEngine差很少。只是做用域不同,它的模塊只對其包含的子context有效。除此,還有一些特殊的邏輯,例如context的部署。Context的部署仍是比較多的,主要分爲:網絡
應該說StandardContext是tomcat中最大的一個類。它封裝的是每一個web app。
看一下StandardContext的主要邏輯單元概念圖。session
org.apache.catalina.session.StandardManager
manager模塊是必需要有的,能夠在server.xml中配置,若是沒有配置的話,會在程序裏生成一個manager對象。
app>web.xml:
<servlet> <servlet-name>httpserver</servlet-name> <servlet-class>com.gearever.servlet.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>httpserver</servlet-name> <url-pattern>/*.do</url-pattern> </servlet-mapping>
對於mapper對象,能夠抽象的理解成一個map結構,其key是某個訪問資源,例如/*.do,那麼其value就是封裝了處理這個資源TestServlet的某個wrapper對象。當訪問/*.do資源時,TestServlet就會在mapper對象中定位到。這裏須要特別說明的是,經過這個mapper對象定位特定的wrapper對象的方式,只有一種狀況,那就是在servlet或jsp中經過forward方式訪問資源時用到。例如,
request.getRequestDispatcher(url).forward(request, response)
關於mapper機制會在一篇文檔中專門說明,這裏簡單介紹一下,方便理解。如圖所示。
Mapper對象在tomcat中存在於兩個地方(注意,不是說只有兩個mapper對象存在),其一,是每一個context容器對象中,它只記錄了此context內部的訪問資源與相對應的wrapper子容器的映射;其二,是connector模塊中,這是tomcat全局的變量,它記錄了一個完整的映射對應關係,即根據訪問的完整URL如何定位到哪一個host下的哪一個context的哪一個wrapper容器。
這樣,經過上面說的forward方式訪問資源會用到第一種mapper,除此以外,其餘的任何方式,都是經過第二種方式的mapper定位到wrapper來處理的。也就是說,forward是服務器內部的重定向,不須要通過網絡接口,所以只須要經過內存中的處理就能完成。這也就是常說的forward與sendRedirect方式重定向區別的根本所在。
看一下request.getRequestDispatcher(url) 方法的源碼。
Request /** * @return a RequestDispatcher that wraps the resource at the specified * path, which may be interpreted as relative to the current request path. * * @param path Path of the resource to be wrapped */ @Override public RequestDispatcher getRequestDispatcher(String path) { Context context = getContext(); if (context == null) { return null; } // If the path is already context-relative, just pass it through if (path == null) { return null; } else if (path.startsWith("/")) { return (context.getServletContext().getRequestDispatcher(path)); } // Convert a request-relative path to a context-relative one String servletPath = (String) getAttribute( RequestDispatcher.INCLUDE_SERVLET_PATH); if (servletPath == null) { servletPath = getServletPath(); } // Add the path info, if there is any String pathInfo = getPathInfo(); String requestPath = null; if (pathInfo == null) { requestPath = servletPath; } else { requestPath = servletPath + pathInfo; } int pos = requestPath.lastIndexOf('/'); String relative = null; if (context.getDispatchersUseEncodedPaths()) { if (pos >= 0) { relative = URLEncoder.DEFAULT.encode( requestPath.substring(0, pos + 1), "UTF-8") + path; } else { relative = URLEncoder.DEFAULT.encode(requestPath, "UTF-8") + path; } } else { if (pos >= 0) { relative = requestPath.substring(0, pos + 1) + path; } else { relative = requestPath + path; } } return context.getServletContext().getRequestDispatcher(relative); }
主要說說servlet對象與servlet stack對象。這兩個對象在wrapper容器中只存在其中之一,也就是說只有其中一個不爲空。當以servlet對象存在時,說明此servlet是支持多線程併發訪問的,也就是說不存在線程同步的過程,此wrapper容器中只包含一個servlet對象(這是咱們經常使用的模式);當以servlet stack對象存在時,說明servlet是不支持多線程併發訪問的,每一個servlet對象任一時刻只有一個線程能夠調用,這樣servlet stack實現的就是個簡易的線程池,此wrapper容器中只包含一組servlet對象,它的基本原型是worker thread模式實現的。
那麼,怎麼來決定是以servlet對象方式存儲仍是servlet stack方式存儲呢?其實,只要在開發servlet類時,實現一個SingleThreadModel接口便可。
public class LoginServlet extends HttpServlet implements javax.servlet.SingleThreadModel
可是值得注意的是,這種同步機制只是從servlet規範的角度來講提供的一種功能,在實際應用中並不能徹底解決線程安全問題,例如若是servlet中有static數據訪問等,所以若是對線程安全又比較嚴格要求的,最好仍是用一些其餘的自定義的解決方案。
Wrapper的基本功能已經說了。那麼再說一個wrapper比較重要的概念。嚴格的說,並非每個訪問資源對應一個wrapper對象。而是每一種訪問資源對應一個wrapper對象。其大體可分爲三種:
org.apache.catalina.servlets.DefaultServlet
org.apache.jasper.servlet.JspServlet
它主要實現了對jsp的編譯等操做
須要注意的是,前兩種wrapper分別是一個,主要是其對應的是DefaultServlet及JspServlet。這兩個servlet是在tomcat的全局conf目錄下的web.xml中配置的,當app啓動時,加載到內存中。
tomcat conf web.xml <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>