Tomcat系統(tǒng)架構(gòu)(下)-容器

容器潘懊,顧名思義就是用來裝載東西的器具姚糊,在 Tomcat 里,容器就是用來裝載 Servlet 的授舟。

容器的層次結(jié)構(gòu)

Tomcat 設(shè)計(jì)了四種容器救恨,分別是 Engine、Host释树、Context 和 Wrapper 肠槽。這四種容器不是平行關(guān)系擎淤,而是父子關(guān)系。如圖:

Tomcat容器層次結(jié)構(gòu)圖.jpg

Tomcat 通過一種分層的結(jié)構(gòu)秸仙,使得 Servlet 容器具有很好的靈活性

Context表示一個(gè)Web應(yīng)用程序嘴拢;Wrapper表示一個(gè)Servlet,一個(gè)Web應(yīng)用中可能會(huì)有多個(gè)Servlet寂纪;Host代表的是一個(gè)虛擬主機(jī)席吴,或者說一個(gè)站點(diǎn),可以給Tomcat配置多個(gè)虛擬主機(jī)地址捞蛋,而一個(gè)虛擬主機(jī)地址下可以部署多個(gè)Web應(yīng)用程序孝冒;Engine表示引擎,用來管理多個(gè)虛擬站點(diǎn)拟杉,一個(gè)Service最多只能有一個(gè)Engine.

在Tomcat的server.xml配置文件中也可以體現(xiàn)這個(gè)關(guān)系

<Server>                    //頂層組件庄涡,可以包括多個(gè)Service
    <Service>               //頂層組件,可包含一個(gè)Engine捣域,多個(gè)連接器
        <Connector>         //連接器組件啼染,代表通信接口
        </Connector>
        <Engine>            //容器組件,一個(gè)Engine組件處理Service中的所有請(qǐng)求焕梅,包含多個(gè)Host
            <Host>          //容器組件迹鹅,處理特定的Host下客戶請(qǐng)求,可包含多個(gè)Context
                <Context>   //容器組件贞言,為特定的Web應(yīng)用處理所有的客戶請(qǐng)求
                </Context>
            </Host>
        </Engine>
    </Service>
</Server>

Tomcat使用了組合模式來管理這些容器斜棚。所有容器組件都實(shí)現(xiàn)了Container接口,因此組合模式可以使得用戶對(duì)單容器對(duì)象和組合容器對(duì)象的使用具有一致性该窗。這里的單容器對(duì)象指的是最底層的Wrapper弟蚀,組合容器對(duì)象指的是上面的Context、Host或者Engine酗失。Container(org.apache.catalina.Container)接口定義如下:

public interface Container extends Lifecycle {
    public String getName();
    public void setName(String name);
    public Container getParent();
    public void setParent(Container container);
    public ClassLoader getParentClassLoader();
    public void setParentClassLoader(ClassLoader parent);
    public void addChild(Container child);
    public void addContainerListener(ContainerListener listener);
    public void addPropertyChangeListener(PropertyChangeListener listener);
    public Container findChild(String name);
    public Container[] findChildren();
    public ContainerListener[] findContainerListeners();
    public void removeChild(Container child);
    public void removeContainerListener(ContainerListener listener);
}

在上面的接口中看到了getParent义钉、SetParent、AddChild和removeChild等方法规肴。用來做對(duì)象的統(tǒng)一管理捶闸。

請(qǐng)求定位Servlet的過程

Mapper組件:將用戶請(qǐng)求的URL定位到一個(gè)Servlet,Mapper組件里保存了Web應(yīng)用的配置信息拖刃,其實(shí)就是容器組件與訪問路徑的映射關(guān)系删壮,比如Host容器里配置的域名、Context容器里的Web應(yīng)用路徑兑牡,以及Wrapper容器里Servlet映射的路徑央碟,可以理解為是一個(gè)多層次的Map。

當(dāng)一個(gè)請(qǐng)求到來時(shí)均函,Mapper組件通過解析請(qǐng)求URl里的域名和路徑亿虽,再到自己保存的Map里去查找菱涤,就能定位到一個(gè)Servlet。一個(gè)請(qǐng)求URL最后只會(huì)定位到一個(gè)Wrapper容器经柴,也就是一個(gè)Servlet狸窘。

匹配過程如圖:

一次訪問URL定位過程Mapper.jpg

請(qǐng)求 http://user.shopping.com:8080/order/buy 定位過程:

首先根據(jù)協(xié)議和端口號(hào)選定Service和Engine

我們知道Tomcat的每個(gè)連接器都監(jiān)聽不同的端口,比如Tomcat默認(rèn)的HTTP連接器監(jiān)聽8080端口坯认、默認(rèn)的AJP連接器監(jiān)聽8009端口。上面的例子中URL訪問的是8080端口氓涣,因此這個(gè)請(qǐng)求會(huì)被HTTP連接器接收牛哺,而一個(gè)連接器是屬于一個(gè)Service組件的,這樣Service組件就確定了劳吠。一個(gè)Service中除了有多個(gè)連接器引润,還有一個(gè)容器組件,具體來說就是一個(gè)Engine容器痒玩,因此也就意味著Engine也確定了淳附。

然后,根據(jù)域名選定host

Service和Engine確定后蠢古,Mapper組件通過URL中的域名去查找相應(yīng)的Host容器奴曙。

之后,根據(jù)URL路徑找到Context組件

Host確定以后草讶,Mapper根據(jù)URL的路徑來匹配相應(yīng)的Web應(yīng)用的路徑

最后洽糟,根據(jù)URL路徑找到Wrapper(Servlet)

Context確定后,Mapper再根據(jù)web.xml中配置的Servlet映射路徑來找到具體的Wrapper和Servlet.

Pipeline-Valve管道

在Tomcat通過一層一層的父子容器找到某個(gè)Servlet來處理請(qǐng)求的過程中堕战,這些父子容器都會(huì)對(duì)請(qǐng)求做一些處理坤溃。連接器中的Adapter會(huì)調(diào)用容器的Service方法來執(zhí)行Servlet,最先拿到請(qǐng)求的是Engine容器嘱丢,Engine容器對(duì)請(qǐng)求做一些處理后薪介,會(huì)把請(qǐng)求傳給自己的子容器Host繼續(xù)處理,以此類推越驻,最后這個(gè)請(qǐng)求會(huì)傳給Wrapper容器汁政,Wrapper容器會(huì)調(diào)用最終的Servlet來處理。這個(gè)調(diào)用過程就是通過Pipeline-Valve管道來實(shí)現(xiàn)的伐谈。

Pipeline-Valve是責(zé)任鏈模式烂完,責(zé)任鏈模式是指在一個(gè)請(qǐng)求處理的過程中有很多處理者依次對(duì)請(qǐng)求進(jìn)行處理,每個(gè)處理者負(fù)責(zé)做自己相應(yīng)的處理诵棵,處理完成之后將再調(diào)用下一個(gè)處理者繼續(xù)處理抠蚣。

Valve標(biāo)識(shí)一個(gè)處理點(diǎn),比如權(quán)限認(rèn)證和記錄日志履澳。Valve接口中的關(guān)鍵方法:

    public interface Valve {
        public Valve getNext();
        public void setNext(Valve valve);
        public void backgroundProcess();
        public void invoke(Request request, Response response)
            throws IOException, ServletException;
        public boolean isAsyncSupported();
}

由于Valve是一個(gè)處理點(diǎn)嘶窄,因此invoke方法就是來處理請(qǐng)求的怀跛,Valve中有g(shù)etNext和setNext方法,有一個(gè)鏈表將Valve連起來柄冲,這個(gè)鏈表就是Pipeline吻谋,如下是Pipeline接口:

public interface Pipeline {
    public Valve getBasic();
    public void setBasic(Valve valve);
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void removeValve(Valve valve);
    public Valve getFirst();
    public boolean isAsyncSupported();
    public Container getContainer();
    public void setContainer(Container container);
    public void findNonAsyncValves(Set<String> result);
}

通過Pipeline中的addValve方法,Pipeline維護(hù)了Valve鏈表现横,Valve可以插入到Pipeline中漓拾,對(duì)請(qǐng)求做某些處理。那為什么在Pipeline中沒有invoke方法呢戒祠?因?yàn)檎麄€(gè)調(diào)用鏈的觸發(fā)是Valve來完成的骇两,Valve完成自己的處理后,調(diào)用getNext.invoke() 來觸發(fā)下一個(gè)Valve 調(diào)用姜盈。

每個(gè)容器都有一個(gè)Pipeline對(duì)象低千,只要觸發(fā)這個(gè)Pipeline的第一個(gè)Valve,這個(gè)容器里的Pipeline中的Valve就都會(huì)被調(diào)用到馏颂。不同的容器的Pipeline是通過getBasic方法來實(shí)現(xiàn)的示血。這個(gè)BasicValve處于Valve鏈表的末端,它是Pipeline中必不可少的一個(gè)Valve救拉,負(fù)責(zé)調(diào)用下層容器的Pipeline里的第一個(gè)Valve难审。整個(gè)調(diào)用過程如圖:

Pipeline調(diào)用示意圖.jpg

整個(gè)調(diào)用過程由連接器中的Adapter觸發(fā),它會(huì)調(diào)用Engine的第一個(gè)Valve:

// calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

Wrapper容器的最后一個(gè)Valve會(huì)創(chuàng)建一個(gè)Filter鏈近上,并調(diào)用doFilter()方法剔宪,最終會(huì)調(diào)到Servlet的service方法。

Value和Filter的區(qū)別

  • Valve是Tomcat的私有機(jī)制壹无,與Tomcat的基礎(chǔ)架構(gòu)/API是緊耦合的葱绒。Servlet API是公有的標(biāo)準(zhǔn),所有的Web容器包括Jetty都支持Filter機(jī)制斗锭。
  • 另一個(gè)重要的區(qū)別是Valve工作的Web容器級(jí)別地淀,攔截所有應(yīng)用的請(qǐng)求;而Servlet Filter工作在應(yīng)用級(jí)別岖是,只能攔截某個(gè)Web應(yīng)用的所有請(qǐng)求帮毁。如果想做整個(gè)Web容器的攔截,必須通過Value來實(shí)現(xiàn).
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豺撑,一起剝皮案震驚了整個(gè)濱河市烈疚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聪轿,老刑警劉巖爷肝,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡灯抛,警方通過查閱死者的電腦和手機(jī)金赦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來对嚼,“玉大人夹抗,你說我怎么就攤上這事∽菔” “怎么了漠烧?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)磨确。 經(jīng)常有香客問我沽甥,道長(zhǎng),這世上最難降的妖魔是什么乏奥? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮亥曹,結(jié)果婚禮上邓了,老公的妹妹穿的比我還像新娘。我一直安慰自己媳瞪,他們只是感情好骗炉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛇受,像睡著了一般句葵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兢仰,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天乍丈,我揣著相機(jī)與錄音,去河邊找鬼把将。 笑死轻专,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的察蹲。 我是一名探鬼主播请垛,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼洽议!你這毒婦竟也來了宗收?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤亚兄,失蹤者是張志新(化名)和其女友劉穎混稽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荚坞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年挑宠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颓影。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡各淀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诡挂,到底是詐尸還是另有隱情碎浇,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布璃俗,位于F島的核電站奴璃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏城豁。R本人自食惡果不足惜苟穆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唱星。 院中可真熱鬧雳旅,春花似錦、人聲如沸间聊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哎榴。三九已至型豁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尚蝌,已是汗流浹背迎变。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驼壶,地道東北人氏豌。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像热凹,于是被迫代替她去往敵國(guó)和親泵喘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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