Tomcat源碼分析 -- Tomcat整體架構(gòu)

本章結(jié)構(gòu)如下:

  • 前言
  • Tomcat頂層結(jié)構(gòu)
  • Server
  • Service
  • Connector
  • Container
  • Tomncat啟動(dòng)流程

一、前言

一般而言唱较,對(duì)于一個(gè)復(fù)雜的系統(tǒng)哥纫,直接扎進(jìn)去看源碼會(huì)是很難受的霉旗,會(huì)浪費(fèi)大量的時(shí)間和腦細(xì)胞,卻得不到理想的效果蛀骇。

這個(gè)時(shí)候厌秒,策略很重要,應(yīng)該明白擅憔,越是復(fù)雜的東西鸵闪,越會(huì)有良好的邏輯和層次,否則開發(fā)者自己估計(jì)過段時(shí)間怕也搞不清了暑诸。

tomcat是一個(gè)很大的系統(tǒng)蚌讼,有復(fù)雜的結(jié)構(gòu),想要了解它个榕,就應(yīng)該順著開發(fā)者設(shè)計(jì)之初的思路來(lái)篡石,先了解整體的結(jié)構(gòu),對(duì)整體有了一定的掌控后西采,再逐個(gè)分析凰萨,了解感興趣的細(xì)節(jié)。

二械馆、Tomcat頂層結(jié)構(gòu)

先上一張圖:

上圖大概展示了tomcat的結(jié)構(gòu)胖眷,下面先簡(jiǎn)單介紹各個(gè)模塊:

  • Server:服務(wù)器的意思,代表整個(gè)tomcat服務(wù)器狱杰,一個(gè)tomcat只有一個(gè)Server瘦材;
  • Service:Server中的一個(gè)邏輯功能層厅须, 一個(gè)Server可以包含多個(gè)Service仿畸;
  • Connector:稱作連接器,是Service的核心組件之一朗和,一個(gè)Service可以有多個(gè)Connector错沽,主要是連接客戶端請(qǐng)求;
  • Container:Service的另一個(gè)核心組件眶拉,按照層級(jí)有Engine千埃,Host,Context忆植,Wrapper四種放可,一個(gè)Service只有一個(gè)Engine谒臼,其主要作用是執(zhí)行業(yè)務(wù)邏輯;
  • Jasper:JSP引擎耀里;
  • Session:會(huì)話管理蜈缤;
  • ...

上面簡(jiǎn)單列了tomcat的模塊結(jié)構(gòu),下面結(jié)合配置文件更加具體一點(diǎn)來(lái)分析冯挎,當(dāng)然更多是集中在Connector和Container兩個(gè)組件上底哥,畢竟這是兩個(gè)核心組件,后續(xù)的內(nèi)容也會(huì)更多集中在這兩個(gè)組件上面房官。

先將conf/server.xml配置文件內(nèi)容貼出供參考(注釋部分沒有貼出):

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml"/>
    </GlobalNamingResources>

    <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443"/>
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
        <Engine name="Catalina" defaultHost="localhost">
            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                       resourceName="UserDatabase"/>
            </Realm>

            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                       prefix="localhost_access_log" suffix=".txt"
                       pattern="%h %l %u %t &quot;%r&quot; %s %b"/>
            </Host>
        </Engine>
    </Service>
</Server>

三趾徽、Server

Server是Tomcat最頂層的容器,代表著整個(gè)服務(wù)器翰守,即一個(gè)Tomcat只有一個(gè)Server孵奶,Server中包含至少一個(gè)Service組件,用于提供具體服務(wù)蜡峰。這個(gè)在配置文件中也得到很好的體現(xiàn)(port="8005" shutdown="SHUTDOWN"是在8005端口監(jiān)聽到"SHUTDOWN"命令拒课,服務(wù)器就會(huì)停止)。

Tomcat中其標(biāo)準(zhǔn)實(shí)現(xiàn)是:org.apache.catalina.core.StandardServer類事示,其繼承結(jié)構(gòu)類圖如下:

StandardServer實(shí)現(xiàn)Server很好理解早像,tomcat為所有的組件都提供了生命周期管理,繼承LifecycleMBeanBase則跟tomcat中的生命周期機(jī)制有關(guān)肖爵,后續(xù)文章會(huì)有介紹卢鹦。

四、Service

可以想象劝堪,一個(gè)Server服務(wù)器冀自,它最基本的功能肯定是:

接收客戶端的請(qǐng)求,然后解析請(qǐng)求秒啦,完成相應(yīng)的業(yè)務(wù)邏輯熬粗,然后把處理后的結(jié)果返回給客戶端,一般會(huì)提供兩個(gè)節(jié)本方法余境,一個(gè)start打開服務(wù)Socket連接驻呐,監(jiān)聽服務(wù)端口,一個(gè)stop停止服務(wù)釋放網(wǎng)絡(luò)資源芳来。

這時(shí)的服務(wù)器就是一個(gè)Server類:

如何實(shí)現(xiàn)這個(gè)簡(jiǎn)單的服務(wù)器含末,看過《深入剖析tomcat》的應(yīng)都知道,這部分代碼之前也敲過即舌,在github上(https://github.com/w1992wishes/tomcat-work)佣盒,其實(shí)就是在一個(gè)端口上監(jiān)聽Socket請(qǐng)求,然后解析請(qǐng)求顽聂,返回處理結(jié)果肥惭。

但如果將請(qǐng)求監(jiān)聽和請(qǐng)求處理放在一起盯仪,擴(kuò)展性會(huì)變差,畢竟網(wǎng)絡(luò)協(xié)議不止HTTP一種蜜葱,如果想適配多種網(wǎng)絡(luò)協(xié)議磨总,請(qǐng)求處理又相同,這時(shí)就無(wú)能為力了笼沥,tomcat的設(shè)計(jì)大師不會(huì)采取這種做法蚪燕,而是將請(qǐng)求監(jiān)聽和請(qǐng)求處理分開為兩個(gè)模塊,分別是Connector和Container奔浅,Connector負(fù)責(zé)處理請(qǐng)求監(jiān)聽馆纳,Container負(fù)責(zé)處理請(qǐng)求處理。

但顯然tomcat可以有多個(gè)Connector汹桦,同時(shí)Container也可以有多個(gè)鲁驶。那這就存在一個(gè)問題,哪個(gè)Connector對(duì)應(yīng)哪個(gè)Container舞骆,提供復(fù)雜的映射嗎钥弯?相信看過server.xml文件的人已經(jīng)知道了tomcat是怎么處理的了。

沒錯(cuò)督禽,Service就是這樣來(lái)的脆霎。在conf/server.xml文件中,可以看到Service組件包含了Connector組件和Engine組件(前面有提過狈惫,Engine就是一種容器)睛蛛,即Service相當(dāng)于Connector和Engine組件的包裝器,將一個(gè)或者多個(gè)Connector和一個(gè)Engine建立關(guān)聯(lián)關(guān)系胧谈。在默認(rèn)的配置文件中忆肾,定義了一個(gè)叫Catalina 的服務(wù),它將HTTP/1.1和AJP/1.3這兩個(gè)Connector與一個(gè)名為Catalina 的Engine關(guān)聯(lián)起來(lái)菱肖。

一個(gè)Server可以包含多個(gè)Service(它們相互獨(dú)立客冈,只是公用一個(gè)JVM及類庫(kù)),一個(gè)Service負(fù)責(zé)維護(hù)多個(gè)Connector和一個(gè)Container稳强。

其標(biāo)準(zhǔn)實(shí)現(xiàn)是StandardService场仲,UML類圖如下:

這時(shí)tomcat就是這樣了:

四、Connector

前面介紹過Connector是連接器键袱,用于接受請(qǐng)求并將請(qǐng)求封裝成Request和Response燎窘,然后交給Container進(jìn)行處理摹闽,Container處理完之后在交給Connector返回給客戶端蹄咖。

一個(gè)Connector會(huì)監(jiān)聽一個(gè)獨(dú)立的端口來(lái)處理來(lái)自客戶端的請(qǐng)求。server.xml默認(rèn)配置了兩個(gè)Connector:

  • <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>付鹿,它監(jiān)聽端口8080,這個(gè)端口值可以修改澜汤,connectionTimeout定義了連接超時(shí)時(shí)間蚜迅,單位是毫秒,redirectPort 定義了ssl的重定向接口俊抵,根據(jù)上述配置谁不,Connector會(huì)將ssl請(qǐng)求轉(zhuǎn)發(fā)到8443端口。
  • <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />, AJP表示Apache Jserv Protocol徽诲,它將處理Tomcat和Apache http服務(wù)器之間的交互刹帕,此連接器用于處理我們將Tomcat和Apache http服務(wù)器結(jié)合使用的情況,如在同一臺(tái)物理Server上部署一個(gè)Apache http服務(wù)器和多臺(tái)Tomcat服務(wù)器谎替,通過Apache服務(wù)器來(lái)處理靜態(tài)資源以及負(fù)載均衡時(shí)偷溺,針對(duì)不同的Tomcat實(shí)例需要AJP監(jiān)聽不同的端口。

Connector在tomcat中的設(shè)計(jì)比較復(fù)雜钱贯,先大致列上一個(gè)圖:

這里先簡(jiǎn)單介紹Connector挫掏,后續(xù)會(huì)詳細(xì)分析。

Connector使用ProtocolHandler來(lái)處理請(qǐng)求的秩命,不同的ProtocolHandler代表不同的連接類型尉共,比如:Http11Protocol使用的是普通Socket來(lái)連接的(tomcat9已經(jīng)刪除了這個(gè)類,不再采用BIO的方式)弃锐,Http11NioProtocol使用的是NioSocket來(lái)連接的袄友。

其中ProtocolHandler由包含了三個(gè)部件:Endpoint、Processor霹菊、Adapter杠河。

  1. Endpoint用來(lái)處理底層Socket的網(wǎng)絡(luò)連接,Processor用于將Endpoint接收到的Socket封裝成Request(這個(gè)Request和ServletRequest無(wú)關(guān))浇辜,Adapter充當(dāng)適配器券敌,用于將Request轉(zhuǎn)換為ServletRequest交給Container進(jìn)行具體的處理。
  2. Endpoint由于是處理底層的Socket網(wǎng)絡(luò)連接柳洋,因此Endpoint是用來(lái)實(shí)現(xiàn)TCP/IP協(xié)議的待诅,而Processor用來(lái)實(shí)現(xiàn)HTTP協(xié)議的,Adapter將請(qǐng)求適配到Servlet容器進(jìn)行具體的處理熊镣。
  3. Endpoint的抽象實(shí)現(xiàn)AbstractEndpoint里面定義的Acceptor和AsyncTimeout兩個(gè)內(nèi)部類和一個(gè)Handler接口卑雁。Acceptor用于監(jiān)聽請(qǐng)求,AsyncTimeout用于檢查異步Request的超時(shí)绪囱,Handler用于處理接收到的Socket测蹲,在內(nèi)部調(diào)用Processor進(jìn)行處理。

先不用太糾結(jié)鬼吵,了解Connector是做什么的就可以扣甲,后續(xù)再深入。

五、Container

tomcat的container層次如下:

5.1琉挖、Engine

一個(gè)Service中有多個(gè)Connector和一個(gè)Engine启泣,Engine表示整個(gè)Servlet引擎,一個(gè)Engine下面可以包含一個(gè)或者多個(gè)Host示辈,即一個(gè)Tomcat實(shí)例可以配置多個(gè)虛擬主機(jī)寥茫,默認(rèn)的情況下 conf/server.xml 配置文件中<Engine name="Catalina" defaultHost="localhost"> 定義了一個(gè)名為Catalina的Engine。

Engine的UML類圖如下:

ContainerBase和LifecycleBase都是抽象出來(lái)的公共層矾麻。

5.2纱耻、Host

Host,代表一個(gè)站點(diǎn)险耀,也可以叫虛擬主機(jī)膝迎,一個(gè)Host可以配置多個(gè)Context,在server.xml文件中的默認(rèn)配置為<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">, 其中appBase=webapps胰耗, 也就是<CATALINA_HOME>\webapps目錄限次,unpackingWARS=true 屬性指定在appBase指定的目錄中的war包都自動(dòng)的解壓,autoDeploy=true 屬性指定對(duì)加入到appBase目錄的war包進(jìn)行自動(dòng)的部署柴灯。

一個(gè)Engine包含多個(gè)Host的設(shè)計(jì)卖漫,使得一個(gè)服務(wù)器實(shí)例可以承擔(dān)多個(gè)域名的服務(wù),是很靈活的設(shè)計(jì)赠群。

其標(biāo)準(zhǔn)實(shí)現(xiàn)繼承圖如下:

5.3羊始、Context

Context,代表一個(gè)應(yīng)用程序查描,就是日常開發(fā)中的web程序突委,或者一個(gè)WEB-INF目錄以及下面的web.xml文件,換句話說(shuō)每一個(gè)運(yùn)行的webapp最終都是以Context的形式存在冬三,每個(gè)Context都有一個(gè)根路徑和請(qǐng)求路徑匀油;與Host的區(qū)別是Context代表一個(gè)應(yīng)用,如勾笆,默認(rèn)配置下webapps下的每個(gè)目錄都是一個(gè)應(yīng)用敌蚜,其中ROOT目錄中存放主應(yīng)用,其他目錄存放別的子應(yīng)用窝爪,而整個(gè)webapps是一個(gè)站點(diǎn)弛车。

在Tomcat中通常采用如下方式創(chuàng)建一個(gè)Context:

  1. 在<CATALINA_HOME>\webapps 目錄中創(chuàng)建一個(gè)目錄dirname,此時(shí)將自動(dòng)創(chuàng)建一個(gè)context蒲每,默認(rèn)context的訪問url為http://host:port/dirname纷跛,也可以通過在ContextRoot\META-INF 中創(chuàng)建一個(gè)context.xml文件,其中包含如下內(nèi)容來(lái)指定應(yīng)用的訪問路徑:<context path="/urlpath">
  2. 在server.xml文件中增加context 元素邀杏,如下:<Context path="/urlpath" docBase="/test/xxx" reloadable=true />

這樣就可以通過http://host:port/urlpath訪問上面配置的應(yīng)用贫奠。

可以打開tomcat目錄對(duì)照一下:

其標(biāo)準(zhǔn)實(shí)現(xiàn)類圖如下:

5.4、Wrapper

一個(gè)Context可以包含多個(gè)Servlet處理不同請(qǐng)求,當(dāng)然現(xiàn)在的SpringMVC叮阅,struts框架的出現(xiàn)導(dǎo)致程序中不再是大量的Servlet刁品,但其實(shí)本質(zhì)是沒變的泣特,都是由Servlet來(lái)處理或者當(dāng)作入口浩姥。

在tomcat中Servlet被稱為wrapper,其標(biāo)準(zhǔn)類圖如下:

那么為什么要用Wrapper來(lái)表示Servlet状您?這和tomcat的處理機(jī)制有關(guān)勒叠,為了更加靈活,便于擴(kuò)展膏孟,tomcat是用管道(pipeline)和閥(valve)的形式來(lái)處理請(qǐng)求眯分,所以將Servlet丟給Wrapper。這個(gè)后續(xù)再分析柒桑。

那么現(xiàn)在tomcat就是這樣的:

六弊决、Tomncat啟動(dòng)流程

tomcat的啟動(dòng)流程很標(biāo)準(zhǔn)化,入口是BootStrap魁淳,統(tǒng)一按照生命周期管理接口Lifecycle的定義進(jìn)行啟動(dòng)飘诗。首先,調(diào)用init()方法逐級(jí)初始化界逛,接著調(diào)用start()方法進(jìn)行啟動(dòng)昆稿,同時(shí),每次調(diào)用伴隨著生命周期狀態(tài)變更事件的觸發(fā)息拜。

每一級(jí)組件除完成自身的處理外溉潭,還有負(fù)責(zé)調(diào)用子組件的相關(guān)調(diào)用,組件和組件之間是松耦合的少欺,可以通過配置進(jìn)行修改喳瓣。

大致流程圖如下:

七、總結(jié)

差不多就是這樣赞别,大概了解總體的結(jié)構(gòu)夫椭,當(dāng)然這里面沒有過多提及除Connector和Container外的其他結(jié)構(gòu),如果感興趣氯庆,可以自行學(xué)習(xí)蹭秋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市堤撵,隨后出現(xiàn)的幾起案子仁讨,更是在濱河造成了極大的恐慌,老刑警劉巖实昨,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洞豁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丈挟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門刁卜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人曙咽,你說(shuō)我怎么就攤上這事蛔趴。” “怎么了例朱?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵孝情,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我洒嗤,道長(zhǎng)箫荡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任渔隶,我火速辦了婚禮羔挡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘间唉。我一直安慰自己绞灼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布终吼。 她就那樣靜靜地躺著镀赌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪际跪。 梳的紋絲不亂的頭發(fā)上商佛,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音姆打,去河邊找鬼良姆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛幔戏,可吹牛的內(nèi)容都是我干的玛追。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼闲延,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痊剖!你這毒婦竟也來(lái)了垒玲?” 一聲冷哼從身側(cè)響起叮贩,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤击狮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后益老,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彪蓬,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年捺萌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了档冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡互婿,死狀恐怖捣郊,靈堂內(nèi)的尸體忽然破棺而出辽狈,到底是詐尸還是另有隱情慈参,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布刮萌,位于F島的核電站驮配,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏着茸。R本人自食惡果不足惜壮锻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涮阔。 院中可真熱鬧猜绣,春花似錦、人聲如沸敬特。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伟阔。三九已至辣之,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間皱炉,已是汗流浹背怀估。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留合搅,地道東北人多搀。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像灾部,于是被迫代替她去往敵國(guó)和親康铭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容