上面簡單列了tomcat的模塊結構,下面結合配置文件更加具體一點來分析,固然更可能是集中在Connector和Container兩個組件上,畢竟這是兩個核心組件,後續的內容也會更多集中在這兩個組件上面html
先將conf/server.xml配置文件內容貼出供參考(註釋部分沒有貼出):java
<?xml version="1.0" encoding="UTF-8"?> --><Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener"/> <!-- Security listener. Documentation at /docs/config/listeners.html <Listener className="org.apache.catalina.security.SecurityListener" /> --> <!--APR library loader. Documentation at /docs/apr.html --> <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/> <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> <Listener className="org.apache.catalina.core.JasperListener"/> <!-- Prevent memory leaks due to use of particular java/javax APIs--> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/> <!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html --> <GlobalNamingResources> <!-- Editable user database that can also be used by UserDatabaseRealm to authenticate users --> <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/> </GlobalNamingResources> <Service name="Catalina"> <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="utf-8"/> <!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/> <!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> --> <Engine defaultHost="localhost" name="Catalina"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log." suffix=".txt"/> <Context docBase="my-web" path="/my-web" reloadable="true" source="org.eclipse.jst.jee.server:my-web"/></Host> </Engine> </Service> </Server>
Server是Tomcat最頂層的容器,表明着整個服務器,即一個Tomcat只有一個Server,Server中包含至少一個Service組件,用於提供具體服務。這個在配置文件中也獲得很好的體現(port=「8005」 shutdown="SHUTDOWN"是在8005端口監聽到"SHUTDOWN"命令,服務器就會中止)。git
Tomcat中其標準實現是:org.apache.catalina.core.StandardServer類,其繼承結構類圖以下:github
StandardServer實現Server很好理解,tomcat爲全部的組件都提供了生命週期管理,繼承LifecycleMBeanBase則跟tomcat中的生命週期機制有關,後續文章會有介紹。web
能夠想象,一個Server服務器,它最基本的功能確定是:apache
接收客戶端的請求,而後解析請求,完成相應的業務邏輯,而後把處理後的結果返回給客戶端,通常會提供兩個節本方法,一個start打開服務Socket鏈接,監聽服務端口,一個stop中止服務釋放網絡資源。tomcat
這時的服務器就是一個Server類:服務器
如何實現這個簡單的服務器,看過《深刻剖析tomcat》的應都知道,這部分代碼以前也敲過,在github上(https://github.com/w1992wishes/tomcat-work),其實就是在一個端口上監聽Socket請求,而後解析請求,返回處理結果。網絡
但若是將請求監聽和請求處理放在一塊兒,擴展性會變差,畢竟網絡協議不止HTTP一種,若是想適配多種網絡協議,請求處理又相同,這時就無能爲力了,tomcat的設計大師不會採起這種作法,而是將請求監聽和請求處理分開爲兩個模塊,分別是Connector和Container,Connector負責處理請求監聽,Container負責處理請求處理。架構
但顯然tomcat能夠有多個Connector,同時Container也能夠有多個。那這就存在一個問題,哪一個Connector對應哪一個Container,提供複雜的映射嗎?相信看過server.xml文件的人已經知道了tomcat是怎麼處理的了。
沒錯,Service就是這樣來的。在conf/server.xml文件中,能夠看到Service組件包含了Connector組件和Engine組件(前面有提過,Engine就是一種容器),即Service至關於Connector和Engine組件的包裝器,將一個或者多個Connector和一個Engine創建關聯關係。在默認的配置文件中,定義了一個叫Catalina 的服務,它將HTTP/1.1和AJP/1.3這兩個Connector與一個名爲Catalina 的Engine關聯起來。
一個Server能夠包含多個Service(它們相互獨立,只是公用一個JVM及類庫),一個Service負責維護多個Connector和一個Container。
其標準實現是StandardService,UML類圖以下:
這時tomcat就是這樣了:
前面介紹過Connector是鏈接器,用於接受請求並將請求封裝成Request和Response,而後交給Container進行處理,Container處理完以後在交給Connector返回給客戶端。
一個Connector會監聽一個獨立的端口來處理來自客戶端的請求。server.xml默認配置了兩個Connector:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>,它監聽端口8080,這個端口值能夠修改,connectionTimeout定義了鏈接超時時間,單位是毫秒,redirectPort 定義了ssl的重定向接口,根據上述配置,Connector會將ssl請求轉發到8443端口。
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />, AJP表示Apache Jserv Protocol,它將處理Tomcat和Apache http服務器之間的交互,此鏈接器用於處理咱們將Tomcat和Apache http服務器結合使用的狀況,如在同一臺物理Server上部署一個Apache http服務器和多臺Tomcat服務器,經過Apache服務器來處理靜態資源以及負載均衡時,針對不一樣的Tomcat實例須要AJP監聽不一樣的端口。
Connector在tomcat中的設計比較複雜,先大體列上一個圖:
這裏先簡單介紹Connector,後續會詳細分析。
Connector使用ProtocolHandler來處理請求的,不一樣的ProtocolHandler表明不一樣的鏈接類型,好比:Http11Protocol使用的是普通Socket來鏈接的(tomcat9已經刪除了這個類,再也不採用BIO的方式),Http11NioProtocol使用的是NioSocket來鏈接的。
其中ProtocolHandler由包含了三個部件:Endpoint、Processor、Adapter。
Endpoint用來處理底層Socket的網絡鏈接,Processor用於將Endpoint接收到的Socket封裝成Request(這個Request和ServletRequest無關),Adapter充當適配器,用於將Request轉換爲ServletRequest交給Container進行具體的處理。
Endpoint因爲是處理底層的Socket網絡鏈接,所以Endpoint是用來實現TCP/IP協議的,而Processor用來實現HTTP協議的,Adapter將請求適配到Servlet容器進行具體的處理。
Endpoint的抽象實現AbstractEndpoint裏面定義的Acceptor和AsyncTimeout兩個內部類和一個Handler接口。Acceptor用於監聽請求,AsyncTimeout用於檢查異步Request的超時,Handler用於處理接收到的Socket,在內部調用Processor進行處理。
先不用太糾結,瞭解Connector是作什麼的就能夠,後續再深刻。
tomcat的container層次以下:
一個Service中有多個Connector和一個Engine,Engine表示整個Servlet引擎,一個Engine下面能夠包含一個或者多個Host,即一個Tomcat實例能夠配置多個虛擬主機,默認的狀況下 conf/server.xml 配置文件中<Engine name="Catalina" defaultHost="localhost"> 定義了一個名爲Catalina的Engine。
ContainerBase和LifecycleBase都是抽象出來的公共層。
Host,表明一個站點,也能夠叫虛擬主機,一個Host能夠配置多個Context,在server.xml文件中的默認配置爲<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">, 其中appBase=webapps, 也就是<CATALINA_HOME>\webapps目錄,unpackingWARS=true 屬性指定在appBase指定的目錄中的war包都自動的解壓,autoDeploy=true 屬性指定對加入到appBase目錄的war包進行自動的部署。
一個Engine包含多個Host的設計,使得一個服務器實例能夠承擔多個域名的服務,是很靈活的設計。
其標準實現繼承圖以下:
Context,表明一個應用程序,就是平常開發中的web程序,或者一個WEB-INF目錄以及下面的web.xml文件,換句話說每個運行的webapp最終都是以Context的形式存在,每一個Context都有一個根路徑和請求路徑;與Host的區別是Context表明一個應用,如,默認配置下webapps下的每一個目錄都是一個應用,其中ROOT目錄中存放主應用,其餘目錄存放別的子應用,而整個webapps是一個站點。
在Tomcat中一般採用以下方式建立一個Context:
在<CATALINA_HOME>\webapps 目錄中建立一個目錄dirname,此時將自動建立一個context,默認context的訪問url爲http://host:port/dirname,也能夠經過在ContextRoot\META-INF 中建立一個context.xml文件,其中包含以下內容來指定應用的訪問路徑:
在server.xml文件中增長context 元素,以下:<Context path="/urlpath" docBase="/test/xxx" reloadable=true />
這樣就能夠經過http://host:port/urlpath訪問上面配置的應用。
能夠打開tomcat目錄對照一下:
其標準實現類圖以下:
一個Context能夠包含多個Servlet處理不一樣請求,固然如今的SpringMVC,struts框架的出現致使程序中再也不是大量的Servlet,但其實本質是沒變的,都是由Servlet來處理或者看成入口。
在tomcat中Servlet被稱爲wrapper,其標準類圖以下:
那麼爲何要用Wrapper來表示Servlet?這和tomcat的處理機制有關,爲了更加靈活,便於擴展,tomcat是用管道(pipeline)和閥(valve)的形式來處理請求,因此將Servlet丟給Wrapper。這個後續再分析。
那麼如今tomcat就是這樣的:
tomcat的啓動流程很標準化,入口是BootStrap,統一按照生命週期管理接口Lifecycle的定義進行啓動。首先,調用init()方法逐級初始化,接着調用start()方法進行啓動,同時,每次調用伴隨着生命週期狀態變動事件的觸發。
每一級組件除完成自身的處理外,還有負責調用子組件的相關調用,組件和組件之間是鬆耦合的,能夠經過配置進行修改。
大體流程圖以下:
差很少就是這樣,大概瞭解整體的結構,固然這裏面沒有過多說起除Connector和Container外的其餘結構,若是感興趣,能夠自行學習。