理解Tomcat工作原理

WEB服務(wù)器

只要Web上的Server都叫Web Server,但是大家分工不同,解決的問(wèn)題也不同,所以根據(jù)Web Server提供的功能,每個(gè)Web Server的名字也會(huì)不一樣。

按功能分類(lèi),Web Server可以分為:

|- Web Server
        |- Http Server
        |- Application Server
            |- Servlet Container
            |- CGI Server
            |- ......

Http服務(wù)器

HTTP Server本質(zhì)上也是一種應(yīng)用程序——它通常運(yùn)行在服務(wù)器之上,綁定服務(wù)器的IP地址并監(jiān)聽(tīng)某一個(gè)tcp端口來(lái)接收并處理HTTP請(qǐng)求,這樣客戶(hù)端(一般來(lái)說(shuō)是IE, Firefox,Chrome這樣的瀏覽器)就能夠通過(guò)HTTP協(xié)議來(lái)獲取服務(wù)器上的網(wǎng)頁(yè)(HTML格式)、文檔(PDF格式)、音頻(MP4格式)、視頻(MOV格式)等等資源。

一個(gè)HTTP Server關(guān)心的是HTTP協(xié)議層面的傳輸和訪(fǎng)問(wèn)控制,所以在A(yíng)pache/Nginx上你可以看到代理、負(fù)載均衡等功能。

客戶(hù)端通過(guò)HTTP Server訪(fǎng)問(wèn)服務(wù)器上存儲(chǔ)的靜態(tài)資源(HTML文件、圖片文件等等)。

通過(guò)CGI/Servlet技術(shù),也可以將處理過(guò)的動(dòng)態(tài)內(nèi)容通過(guò)HTTP Server分發(fā),但是一個(gè)HTTP Server始終只是把服務(wù)器上的文件如實(shí)的通過(guò)HTTP協(xié)議傳輸給客戶(hù)端。

HTTP Server中經(jīng)常使用的是Apache、Nginx兩種,HTTP Server主要用來(lái)做靜態(tài)內(nèi)容服務(wù)、代理服務(wù)器、負(fù)載均衡等。直面外來(lái)請(qǐng)求轉(zhuǎn)發(fā)給后面的應(yīng)用服務(wù)(Tomcat,django什么的)。

|- Http Server
    |- Apache
    |- Nginx

Application Server

Application Server 是一個(gè)應(yīng)用執(zhí)行的服務(wù)器。它首先需要支持開(kāi)發(fā)語(yǔ)言的 Runtime(對(duì)于 Tomcat 來(lái)說(shuō),就是 Java),保證應(yīng)用能夠在應(yīng)用服務(wù)器上正常運(yùn)行。其次,需要支持應(yīng)用相關(guān)的規(guī)范,例如類(lèi)庫(kù)、安全方面的特性。與HTTP Server相比,Application Server能夠動(dòng)態(tài)的生成資源并返回到客戶(hù)端。

|- Application Server
    |- Tomcat
    |- Jetty

當(dāng)初在A(yíng)pache Server開(kāi)發(fā)時(shí)還未出現(xiàn)Servlet的概念,所以Apache不能內(nèi)置支持Servlet。實(shí)際上,除了Apache,其他許多HTTP Server軟件都不能直接支持Servlet。為了支持Servlet,通常要單獨(dú)開(kāi)發(fā)程序,這種程序一般稱(chēng)為服務(wù)器小程序容器(Servlet Container),有時(shí)也叫做服務(wù)器小程序引擎(Servlet Engine)。它是Web服務(wù)器或應(yīng)用程序服務(wù)器的一部分,用于在發(fā)送的請(qǐng)求和響應(yīng)之上提供網(wǎng)絡(luò)服務(wù),解碼基于MIME的請(qǐng)求,格式化基于MIME的響應(yīng),它在Servlet的生命周期內(nèi)包容和管理Servlet,是一個(gè)實(shí)時(shí)運(yùn)行的外殼程序。運(yùn)行時(shí)由Web服務(wù)器軟件處理一般請(qǐng)求,并把Servlet調(diào)用傳遞給“容器”來(lái)處理。

比如,對(duì)于 Tomcat 來(lái)說(shuō),就是需要提供 JSP/Sevlet 運(yùn)行需要的標(biāo)準(zhǔn)類(lèi)庫(kù)、Interface 等。為了方便,應(yīng)用服務(wù)器往往也會(huì)集成 HTTP Server 的功能,但是不如專(zhuān)業(yè)的 HTTP Server 那么強(qiáng)大,所以Application Server往往是運(yùn)行在 HTTP Server 的背后,執(zhí)行應(yīng)用,將動(dòng)態(tài)的內(nèi)容轉(zhuǎn)化為靜態(tài)的內(nèi)容之后,通過(guò) HTTP Server 分發(fā)到客戶(hù)端。

Tomcat運(yùn)行在JVM之上,它和HTTP服務(wù)器一樣,綁定IP地址并監(jiān)聽(tīng)TCP端口,同時(shí)還包含以下指責(zé):

1. 管理Servlet程序的生命周期;
2. 將URL映射到指定的Servlet進(jìn)行處理;
3. 與Servlet程序合作處理HTTP請(qǐng)求——根據(jù)HTTP請(qǐng)求生成HttpServletRequest/Response對(duì)象并傳遞給Servlet進(jìn)行處理,將Servlet中的HttpServletResponse對(duì)象生成的內(nèi)容返回給瀏覽器;

所以 Tomcat 屬于是一個(gè)「Application Server」,但是更準(zhǔn)確的來(lái)說(shuō),是一個(gè)「Servlet/JSP」應(yīng)用的容器(Ruby/Python 等其他語(yǔ)言開(kāi)發(fā)的應(yīng)用也無(wú)法直接運(yùn)行在 Tomcat 上)。

Tomcat工作原理

Tomcat 的結(jié)構(gòu)很復(fù)雜,但是 Tomcat 也非常的模塊化,找到了 Tomcat 最核心的模塊,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的總體結(jié)構(gòu)圖:

Tomcat的總體結(jié)構(gòu)圖

從上圖可以看出Tomcat的核心是兩個(gè)組件:連接器(Connector)容器(Container)。Connector組件是負(fù)責(zé)生成請(qǐng)求對(duì)象和響應(yīng)對(duì)象的,Tomcat默認(rèn)的是HttpConnector,負(fù)責(zé)根據(jù)收到的Http請(qǐng)求報(bào)文生成Request對(duì)象和Response對(duì)象,并把這兩個(gè)對(duì)象傳遞給Container,然后根據(jù)Response中的內(nèi)容生成相應(yīng)的HTTP報(bào)文。

Container是容器的父接口,所有子容器都必須實(shí)現(xiàn)這個(gè)接口,簡(jiǎn)單來(lái)說(shuō)就是服務(wù)器部署的項(xiàng)目是運(yùn)行在Container中的。Container里面的項(xiàng)目獲取到Connector傳遞過(guò)來(lái)對(duì)應(yīng)的的Request對(duì)象和Response對(duì)象進(jìn)行相應(yīng)的操作。

Connector可以根據(jù)不同的設(shè)計(jì)和應(yīng)用場(chǎng)景進(jìn)行替換。一個(gè)Container可以選擇對(duì)應(yīng)多個(gè)Connector。多個(gè)Connector和一個(gè)Container就形成了一個(gè)Service,有了Service就可以對(duì)外提供服務(wù)了。

Tomcat要為一個(gè)Servlet的請(qǐng)求提供服務(wù),需要做四件事:

  1. 創(chuàng)建一個(gè)request對(duì)象并填充那些有可能被所引用的Servlet使用的信息,如參數(shù),頭部、cookies、查詢(xún)字符串等。一個(gè)request對(duì)象就是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一個(gè)實(shí)例。
  2. 創(chuàng)建一個(gè)response對(duì)象,所引用的servlet使用它來(lái)給客戶(hù)端發(fā)送響應(yīng)。一個(gè)response對(duì)象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的一個(gè)實(shí)例。
  3. 調(diào)用servlet的service方法,并傳入request和response對(duì)象。這里servlet會(huì)從request對(duì)象取值,給response寫(xiě)值。
  4. 根據(jù)servlet返回的response生成相應(yīng)的HTTP響應(yīng)報(bào)文。

既然我們已經(jīng)抓到Tomcat的“七寸”,兩個(gè)核心組件:連接器(Connector)和容器(Container),那這樣從連接器(Connector)入手,來(lái)看下Tomcat處理HTTP請(qǐng)求的流程。

很多開(kāi)源應(yīng)用服務(wù)器都是集成tomcat作為web container的,而且對(duì)于tomcat的servlet container這部分代碼很少改動(dòng)。這樣,這些應(yīng)用服務(wù)器的性能基本上就取決于Tomcat處理HTTP請(qǐng)求的connector模塊的性能。

Tomcat架構(gòu)模塊

Tomcat架構(gòu)模塊
  1. Server(服務(wù)器)是Tomcat構(gòu)成的頂級(jí)構(gòu)成元素,所有一切均包含在Server中,Server的實(shí)現(xiàn)類(lèi)StandardServer可以包含一個(gè)到多個(gè)Services;
  2. 次頂級(jí)元素Service的實(shí)現(xiàn)類(lèi)為StandardService調(diào)用了容器(Container)接口,其實(shí)是調(diào)用了Servlet Engine(引擎),而且StandardService類(lèi)中也指明了該Service歸屬的Server;
  3. 接下來(lái)次級(jí)的構(gòu)成元素就是容器(Container):主機(jī)(Host)、上下文(Context)和引擎(Engine)均繼承自Container接口,所以它們都是容器。但是,它們是有父子關(guān)系的,在主機(jī)(Host)、上下文(Context)和引擎(Engine)這三類(lèi)容器中,引擎是頂級(jí)容器,直接包含是主機(jī)容器,而主機(jī)容器又包含上下文容器,所以引擎、主機(jī)和上下文從大小上來(lái)說(shuō)又構(gòu)成父子關(guān)系,雖然它們都繼承自Container接口。
  4. 連接器(Connector)將Service和Container連接起來(lái),首先它需要注冊(cè)到一個(gè)Service,它的作用就是把來(lái)自客戶(hù)端的請(qǐng)求轉(zhuǎn)發(fā)到Container(容器),這就是它為什么稱(chēng)作連接器的原因。
Tomcat運(yùn)行流程
Tomcat運(yùn)行流程

假設(shè)來(lái)自客戶(hù)的請(qǐng)求為:http://localhost:8080/test/index.jsp

  1. 請(qǐng)求被發(fā)送到本機(jī)端口8080,被在那里偵聽(tīng)的Coyote HTTP/1.1 Connector獲得;

  2. Connector把該請(qǐng)求交給它所在的Service的Engine來(lái)處理,并等待Engine的回應(yīng);

  3. Engine獲得請(qǐng)求localhost:8080/test/index.jsp,匹配它所有虛擬主機(jī)Host;

  4. Engine匹配到名為localhost的Host(即使匹配不到也把請(qǐng)求交給該Host處理,因?yàn)樵揌ost被定義為該Engine的默認(rèn)主機(jī));

  5. localhost Host獲得請(qǐng)求/test/index.jsp,匹配它所擁有的所有Context;

  6. Host匹配到路徑為/test的Context(如果匹配不到就把該請(qǐng)求交給路徑名為""的Context去處理);

  7. path="/test"的Context獲得請(qǐng)求/index.jsp,在它的mapping table中尋找對(duì)應(yīng)的servlet;

  8. Context匹配到URL PATTERN為*.jsp的servlet,對(duì)應(yīng)于JspServlet類(lèi);

  9. 構(gòu)造HttpServletRequest對(duì)象和HttpServletResponse對(duì)象,作為參數(shù)調(diào)用JspServlet的doGet或doPost方法;

  10. Context把執(zhí)行完了之后的HttpServletResponse對(duì)象返回給Host;

  11. Host把HttpServletResponse對(duì)象返回給Engine;

  12. Engine把HttpServletResponse對(duì)象返回給Connector;

  13. Connector把HttpServletResponse對(duì)象返回給客戶(hù)browser;

Connector 組件

Connector 組件是 Tomcat 中兩個(gè)核心組件之一,它的主要任務(wù)是負(fù)責(zé)接收瀏覽器的發(fā)過(guò)來(lái)的 tcp 連接請(qǐng)求,創(chuàng)建一個(gè) Request 和 Response 對(duì)象分別用于和請(qǐng)求端交換數(shù)據(jù),然后會(huì)產(chǎn)生一個(gè)線(xiàn)程來(lái)處理這個(gè)請(qǐng)求并把產(chǎn)生的 Request 和 Response 對(duì)象傳給處理這個(gè)請(qǐng)求的線(xiàn)程,處理這個(gè)請(qǐng)求的線(xiàn)程就是 Container 組件要做的事了。

由于這個(gè)過(guò)程比較復(fù)雜,大體的流程可以用下面的順序圖來(lái)解釋?zhuān)?/p>

Connector 處理一次請(qǐng)求順序圖

Tomcat5 中默認(rèn)的 Connector 是 Coyote,這個(gè) Connector 是可以選擇替換的。Connector 最重要的功能就是接收連接請(qǐng)求然后分配線(xiàn)程讓 Container 來(lái)處理這個(gè)請(qǐng)求,所以這必然是多線(xiàn)程的,多線(xiàn)程的處理是 Connector 設(shè)計(jì)的核心。

當(dāng) Connector 將 socket 連接封裝成 request 和 response 對(duì)象后接下來(lái)的事情就交給 Container 來(lái)處理了。

Servlet 容器“Container”

Container 是容器的父接口,所有子容器都必須實(shí)現(xiàn)這個(gè)接口,Container 容器的設(shè)計(jì)用的是典型的責(zé)任鏈的設(shè)計(jì)模式,它有四個(gè)子容器組件構(gòu)成,分別是:Engine、Host、Context、Wrapper,這四個(gè)組件不是平行的,而是父子關(guān)系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一個(gè) Servlet class 對(duì)應(yīng)一個(gè) Wrapper,如果有多個(gè) Servlet 就可以定義多個(gè) Wrapper,如果有多個(gè) Wrapper 就要定義一個(gè)更高的 Container 了,如 Context,Context 通常就是對(duì)應(yīng)下面這個(gè)配置:

<Context  
    path="/library"
    docBase="D:\projects\library\deploy\target\library.war"
    reloadable="true" 
/>
容器的總體設(shè)計(jì)

Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運(yùn)行 war 程序,就必須要 Host,因?yàn)?war 中必有 web.xml 文件,這個(gè)文件的解析就需要 Host 了,如果要有多個(gè) Host 就要定義一個(gè) top 容器 Engine 了。而 Engine 沒(méi)有父容器了,一個(gè) Engine 代表一個(gè)完整的 Servlet 引擎。

當(dāng) Connector 接受到一個(gè)連接請(qǐng)求時(shí),將請(qǐng)求交給 Container,Container 是如何處理這個(gè)請(qǐng)求的?這四個(gè)組件是怎么分工的,怎么把請(qǐng)求傳給特定的子容器的呢?又是如何將最終的請(qǐng)求交給 Servlet 處理。下面是這個(gè)過(guò)程的時(shí)序圖:

Engine 和 Host 處理請(qǐng)求的時(shí)序圖

參考

WEB請(qǐng)求處理三:Servlet容器請(qǐng)求處理

Tomcat 系統(tǒng)架構(gòu)與設(shè)計(jì)模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

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