Tomcat之組件及架構(gòu)

tomcat 異常復(fù)雜, 組件巨多. 我認(rèn)為, 作為初學(xué)者, 我們不能直接進(jìn)入源碼, 過多的實(shí)現(xiàn)細(xì)節(jié)會掩蓋抽象概念, 容易一葉障目不見泰山

什么是 tomcat ?

tomcat 是一個(gè)接受 http 請求并解析 http 請求并反饋客戶端的一個(gè)應(yīng)用程序.

tomcat 可以說是 sun Servlet 的一個(gè)官方參考實(shí)現(xiàn), 因?yàn)?tomcat 剛開始就是 sun 開發(fā)的,后來捐獻(xiàn)給了 apache 基金會.

在我們理解 tomcat 之前, 我們得了解一下tomcat 所實(shí)現(xiàn)的 Servlet 是什么東東?

那要從互聯(lián)網(wǎng)大潮興起開始講起, 我們知道, 網(wǎng)頁不止有靜態(tài)的還有動態(tài)的, 而在剛開始的時(shí)候, 常見的實(shí)現(xiàn)動態(tài)網(wǎng)頁的技術(shù)就是 CGI, 但是作為 Java 的發(fā)明人, SUN|肯定要搞一個(gè)超級大風(fēng)頭, 讓整個(gè)世界一下子認(rèn)識了 Java, 不過很快悲催的發(fā)現(xiàn) Applet 其實(shí)用途不大, 眼看著互聯(lián)網(wǎng)開始流行, 一定要搭上千載難逢的快車啊.

于是, Servlet 就誕生了, Servlet 其實(shí)就是 SUN 為了讓 Java 能實(shí)現(xiàn)動態(tài)的可交互的網(wǎng)頁, 從而進(jìn)入 web 編程的領(lǐng)域而定義的一套標(biāo)準(zhǔn).

這套標(biāo)準(zhǔn)是這么說的: 你想用 Java 開發(fā)動態(tài)網(wǎng)頁, 可以定義一個(gè)自己的” Servlet”, 但一定要實(shí)現(xiàn)我的 HTTPServlet 接口, 然后重載 doGet(), doPost()方法. 用戶從流浪器 GET 的時(shí)候, 調(diào)用 doGet 方法, 從瀏覽器向服務(wù)器發(fā)送表單數(shù)據(jù)的時(shí)候, 調(diào)用 doPost 方法, 如果你想訪問用戶從瀏覽器傳遞過來的參數(shù), 用 HttpServletRequest 對象就好了, 里面有 getParameter, getQueryString 方法, 如果你處理完了, 想向?yàn)g覽器返回?cái)?shù)據(jù), 用 HttpServletResponse 對象調(diào)用 getPrintWriter 方法就可以輸出數(shù)據(jù)了.

如果你想實(shí)現(xiàn)一個(gè)購物車, 需要 session, 很簡單, 從HttpServletRequest 調(diào)用 getSession 方法就可以了.

Servlet 是如何工作的

servlet 是一個(gè)復(fù)雜的系統(tǒng), 但是他有三個(gè)基本任務(wù), 對每個(gè)請求, servlet 容器會為其完成以下3個(gè)操作:

  1. 創(chuàng)建一個(gè) Request 對象, 用可能會在調(diào)用的 Servlet 中使用到的信息填充該 Request 對象, 如參數(shù), 頭, cookie, 查詢字符串, URI 等, Request 對象是 javax.servlet.ServletRequest 接口或 javax.servlet.ServletRequest 接口的一個(gè)實(shí)例.

  2. 創(chuàng)建一個(gè)調(diào)用 Servlet 的 Response 對象, 用來向 Web 客戶端發(fā)送響應(yīng). response 對象是 javax.servlet.http.ServletResponse 接口或 javax.servlet.ServletResponse 接口的一個(gè)實(shí)例;

  3. 調(diào)用 Servlet 的 service 方法, 將 request 對象和 response 對象作為參數(shù)傳入, Servlet 從 request 對象中讀取信息, 并通過 response 對象發(fā)送響應(yīng)信息.

你寫了一個(gè)” Servlet”, 接下來要運(yùn)行, 你就發(fā)現(xiàn)沒法通過 java 直接運(yùn)行了, 你需要一個(gè)能夠運(yùn)行 Servlet 的容器, 這個(gè)容器 Sun 最早實(shí)現(xiàn)了一個(gè), 叫 Java Web Server, 1999年捐給了 Apache Software foundation, 就改名叫 Tomcat.

所以, Tomcat 就是一個(gè) Servlet 容器, 能接收用戶從瀏覽器發(fā)來的請求, 然后轉(zhuǎn)發(fā)給 Servlet 處理, 把處理完的響應(yīng)數(shù)據(jù)發(fā)回瀏覽器.

但是 Servlet 輸出 Html, 還是采用了老的 CGI 方式 , 是一句一句輸出, 所以,編寫和修改 html 非常不方便, 于是, Java Server Pages(JSP) 就來救急了, JSP 并沒有增加任何本質(zhì)上不能用 Servlet 實(shí)現(xiàn)的功能, 實(shí)際上 JSP 在運(yùn)行之前, 需要先編譯成 Servlet, 然后才執(zhí)行的.

但是, 在 JSP 中編寫靜態(tài) HTML 更加方便, 不必再用 println 語句來輸出每一行HTML 代碼, 更重要的是, 借助內(nèi)容和外觀的分離,頁面制作中不同性質(zhì)的任務(wù)可以方便的分開: 比如, 有頁面設(shè)計(jì)者進(jìn)行 HTML 設(shè)計(jì), 同時(shí)流出供 Java 程序插入動態(tài)內(nèi)容的空間.

Tomcat 能運(yùn)行 Servlet, 當(dāng)然能運(yùn)行 JSP 了.

既然是 Web 服務(wù)器, tomcat 除了能運(yùn)行 Servlet 和 JSP 之外, 也能像 Apache/nginx 一樣, 支持靜態(tài) html, 圖片, 文檔的訪問, 只是性能差一點(diǎn),在實(shí)際的應(yīng)用中, 一般是 Apache/nginx 作為負(fù)載均衡服務(wù)器和靜態(tài)資源服務(wù)器放在最前端, 后面是 tomcat 組成的集群.

如果用戶請求的是靜態(tài)資源, nginx 直接搞定, 如果是動態(tài)資源(如xxx.jsp), nginx 就會按照一定的算法轉(zhuǎn)發(fā)的某個(gè) tomcat 上, 達(dá)到負(fù)載均衡的目的.

Tomcat 是如何設(shè)計(jì)的? 有哪些所謂的組件?

tomcat 的主體是 Catalina, catalina 是一個(gè)成熟的軟件, 設(shè)計(jì)和開發(fā)的十分優(yōu)雅, 功能結(jié)構(gòu)也是模塊化的. 我們之前說 Servlet 是如何工作的?中提到了 Servlet 容器的任務(wù), 基于這些任務(wù)可以將 Catalina 劃分為兩個(gè)模塊: 連接器(connector)和容器(container).

注意: 連接器和 Servlet 容器是一對多的關(guān)系.

連接器負(fù)責(zé)將一個(gè)請求與容器相關(guān)聯(lián), 他的工作為它接收到的每個(gè) http 請求創(chuàng)建一個(gè) Request 對象和一個(gè) Response 對象, 然后, 他將處理過程交給容器. 容器從連接器中接收到 Request 對象和 Response 對象, 并負(fù)責(zé)調(diào)用相應(yīng)的 service 方法.

但是請記住, 上面所描述的處理過程只是 Catalina 容器處理請求的的整個(gè)過程的一小部分, 猶如冰山的一角, 在容器中還包括很多其他的事情要做, 例如, 在容器調(diào)用相應(yīng)的 Servlet 的 service 方法之前, 他必須先載入該 Servlet 類, 對用戶進(jìn)行身份驗(yàn)證(如果有必要的話), 為用戶更新會話信息等, 因此, 當(dāng)你發(fā)現(xiàn)容器使用了很多不同的模塊來處理這些事情時(shí), 請不要它驚訝, 例如, 管理器模塊用來處理用戶會話信息, 類載入器模塊用來載入所需的 Servlet 類.

image.png
Tomcat 整體的框架層次:4個(gè)層次, 其中 Connector 和 Container 是最重要的.
  1. Server 和 Service
  2. Connector
        *  HTTP
        * AJP (apache 私有協(xié)議苟鸯,用于tomcat和apache靜態(tài)服務(wù)器通信)
  1. Container
Engine
Host
Context
Wrapper
  1. Component
Manager (管理器)
logger (日志管理)
loader (載入器)
pipeline (管道)
valve (管道中的閥)

下面我們來看一張有名的關(guān)于 tomcat 的結(jié)構(gòu)圖:


image.png

注意: Server是整個(gè)Tomcat組件的容器吻谋,包含一個(gè)或多個(gè)Service。 Service:Service是包含Connector和Container的集合檀头,Service用適當(dāng)?shù)腃onnector接收用戶的請求,再發(fā)給相應(yīng)的Container來處理。

從這張圖中可以看到,Tomcat的核心組件就兩個(gè)Connector和Container(后面還有詳細(xì)說明),多個(gè)Connector+一個(gè)Container構(gòu)成一個(gè)Service谈截,Service就是對外提供服務(wù)的組件筷屡,有了Service組件Tomcat就可以對外提供服務(wù)了,但是光有服務(wù)還不行簸喂,還得有環(huán)境讓你提供服務(wù)才行毙死,所以最外層的Server就為Service提供了生存的土壤。那么這些個(gè)組件到底是干嘛用的呢喻鳄?Connector是一個(gè)連接器扼倘,主要負(fù)責(zé)接收請求并把請求交給Container,Container就是一個(gè)容器除呵,主要裝的是具體處理請求的組件再菊。Service主要是為了關(guān)聯(lián)Container與Connector,一個(gè)單獨(dú)的Container或者一個(gè)單獨(dú)的Connector都不能完整處理一個(gè)請求颜曾,只有兩個(gè)結(jié)合在一起才能完成一個(gè)請求的處理纠拔。Server這是負(fù)責(zé)管理Service集合,從圖中我們看到一個(gè)Tomcat可以提供多種服務(wù)泛豪,那么這些Serice就是由Server來管理的稠诲,具體的工作包括:對外提供一個(gè)接口訪問Service,對內(nèi)維護(hù)Service集合诡曙,維護(hù)Service集合又包括管理Service的生命周期臀叙、尋找一個(gè)請求的Service、結(jié)束一個(gè)Service等

Connector 組件:

Tomcat都是在容器里面處理問題的价卤, 而容器又到哪里去取得輸入信息呢劝萤? Connector就是專干這個(gè)的。 他會把從socket傳遞過來的數(shù)據(jù)慎璧, 封裝成Request, 傳遞給容器來處理稳其。 通常我們會用到兩種Connector,一種叫http connectoer驶赏, 用來傳遞http需求的。 另一種叫AJP既鞠, 在我們整合apache與tomcat工作的時(shí)候煤傍,apache與tomcat之間就是通過這個(gè)協(xié)議來互動的。 (說到apache與tomcat的整合工作嘱蛋, 通常我們的目的是為了讓apache 獲取靜態(tài)資源蚯姆, 而讓tomcat來解析動態(tài)的jsp或者servlet。)

總的來說洒敏,Connector就是解析Http或Ajp請求的龄恋。

Container 組件:

我們剛剛看到, 容器從大到小分別是 Engine, Host, Context, Wrapper, 從左到右每個(gè)容器都是一對多關(guān)系, 也就是說, Engine 容器可以有多個(gè) Host 容器, Host 容器可以有多個(gè) Context 容器. Context 容器可以有多個(gè) Wrapper 容器.

我們來看看每個(gè)組件的解釋:

  1. Container:可以理解為處理某類型請求的容器,處理的方式一般為把處理請求的處理器包裝為Valve(閥門)對象凶伙,并按一定順序放入類型為Pipeline(管道)的管道里郭毕。Container有多種子類型:Engine、Host函荣、Context和Wrapper显押,這幾種子類型Container依次包含,處理不同粒度的請求傻挂。另外Container里包含一些基礎(chǔ)服務(wù)乘碑,如Loader、Manager和Realm金拒。

  2. Engine:Engine包含Host和Context兽肤,接到請求后仍給相應(yīng)的Host在相應(yīng)的Context里處理。

  3. Host:就是我們所理解的虛擬主機(jī)绪抛。

  4. Context:就是我們所部屬的具體Web應(yīng)用的上下文资铡,每個(gè)請求都在是相應(yīng)的上下文里處理的。

  5. Wrapper:Wrapper是針對每個(gè)Servlet的Container幢码,每個(gè)Servlet都有相應(yīng)的Wrapper來管理害驹。 可以看出Server、Service蛤育、Connector宛官、Container、Engine瓦糕、Host底洗、Context和Wrapper這些核心組件的作用范圍是逐層遞減,并逐層包含咕娄。 下面就是些被Container所用的基礎(chǔ)組件:

Loader:是被Container用來載入各種所需的Class亥揖。
Manager:是被Container用來管理Session池。
Realm:是用來處理安全里授權(quán)與認(rèn)證。

Component 組件:

需求被傳遞到了容器里面费变, 在合適的時(shí)候摧扇, 會傳遞給下一個(gè)容器處理。而容器里面又盛裝著各種各樣的組件挚歧, 我們可以理解為提供各種各樣的增值服務(wù)扛稽。比如:

  1. manager: 當(dāng)一個(gè)容器里面裝了manager組件后,這個(gè)容器就支持session管理了滑负, 事實(shí)上在tomcat里面的session管理, 就是靠的在context里面裝的manager component.

  2. logger: 當(dāng)一個(gè)容器里面裝了logger組件后在张, 這個(gè)容器里所發(fā)生的事情, 就被該組件記錄下來, 我們通常會在logs/ 這個(gè)目錄下看見catalina_log.time.txt 以及l(fā)ocalhost.time.txt和localhost_examples_log.time.txt矮慕。 這就是因?yàn)槲覀兎謩e為:engin, host以及context(examples)這三個(gè)容器安裝了logger組件帮匾, 這也是默認(rèn)安裝, 又叫做標(biāo)配 .

  3. loader: loader這個(gè)組件通常只會給我們的context容器使用痴鳄,loader是用來啟動context以及管理這個(gè)context的classloader用的瘟斜。

  4. pipline: pipeline是這樣一個(gè)東西,使用的責(zé)任鏈模式. 當(dāng)一個(gè)容器決定了要把從上級傳遞過來的需求交給子容器的時(shí)候痪寻, 他就把這個(gè)需求放進(jìn)容器的管道(pipeline)里面去螺句。 而需求傻呼呼得在管道里面流動的時(shí)候, 就會被管道里面的各個(gè)閥門攔截下來槽华。 比如管道里面放了兩個(gè)閥門。 第一個(gè)閥門叫做“access_allow_vavle”趟妥, 也就是說需求流過來的時(shí)候猫态,它會看這個(gè)需求是哪個(gè)IP過來的, 如果這個(gè)IP已經(jīng)在黑名單里面了披摄,sure, 殺亲雪! 第二個(gè)閥門叫做“defaul_access_valve”它會做例行的檢查, 如果通過的話疚膊,OK义辕, 把需求傳遞給當(dāng)前容器的子容器。 就是通過這種方式寓盗, 需求就在各個(gè)容器里面?zhèn)鬟f灌砖,流動, 最后抵達(dá)目的地的了傀蚌。

  5. valve: 就是上面所說的閥門基显。

Tomcat里面大概就是這么些東西, 我們可以簡單地這么理解tomcat的框架善炫,它是一種自上而下撩幽, 容器里又包含子容器的這樣一種結(jié)構(gòu)。

換個(gè)角度再來看看 Tomcat 的總體架構(gòu):
  • 面向組件架構(gòu)
  • 基于JMX
  • 事件偵聽
  1. 面向組件架構(gòu) tomcat代碼看似很龐大,但從結(jié)構(gòu)上看卻很清晰和簡單窜醉,它主要由一堆組件組成宪萄,如Server、Service榨惰、Connector等拜英,并基于JMX管理這些組件,另外實(shí)現(xiàn)以上接口的組件也實(shí)現(xiàn)了代表生存期的接口Lifecycle读串,使其組件履行固定的生存期聊记,在其整個(gè)生存期的過程中通過事件偵聽LifecycleEvent實(shí)現(xiàn)擴(kuò)展。Tomcat的核心類圖如下所示:
image.png
  1. 基于JMX Tomcat會為每個(gè)組件進(jìn)行注冊過程恢暖,通過Registry管理起來排监,而Registry是基于JMX來實(shí)現(xiàn)的,因此在看組件的init和start過程實(shí)際上就是初始化MBean和觸發(fā)MBean的start方法杰捂,會大量看到形如: Registry.getRegistry(null, null).invoke(mbeans, “init”, false); Registry.getRegistry(null, null).invoke(mbeans, “start”, false); 這樣的代碼舆床,這實(shí)際上就是通過JMX管理各種組件的行為和生命期。

那么, 什么是 JMX 呢?
理解jmx

  1. 事件偵聽(觀察者模式/事件驅(qū)動) 各個(gè)組件在其生命期中會有各種各樣行為嫁佳,而這些行為都有觸發(fā)相應(yīng)的事件挨队,Tomcat就是通過偵聽這些時(shí)間達(dá)到對這些行為進(jìn)行擴(kuò)展的目的。在看組件的init和start過程中會看到大量如: lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);這樣的代碼蒿往,這就是對某一類型事件的觸發(fā)盛垦,如果你想在其中加入自己的行為,就只用注冊相應(yīng)類型的事件即可瓤漏。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腾夯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔬充,更是在濱河造成了極大的恐慌蝶俱,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饥漫,死亡現(xiàn)場離奇詭異榨呆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)庸队,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門积蜻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彻消,你說我怎么就攤上這事浅侨。” “怎么了证膨?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵如输,是天一觀的道長。 經(jīng)常有香客問我,道長不见,這世上最難降的妖魔是什么澳化? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮稳吮,結(jié)果婚禮上缎谷,老公的妹妹穿的比我還像新娘。我一直安慰自己灶似,他們只是感情好列林,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酪惭,像睡著了一般希痴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上春感,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天砌创,我揣著相機(jī)與錄音,去河邊找鬼鲫懒。 笑死嫩实,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窥岩。 我是一名探鬼主播甲献,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颂翼!你這毒婦竟也來了晃洒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤疚鲤,失蹤者是張志新(化名)和其女友劉穎锥累,沒想到半個(gè)月后缘挑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體集歇,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年语淘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诲宇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惶翻,死狀恐怖姑蓝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吕粗,我是刑警寧澤纺荧,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響宙暇,放射性物質(zhì)發(fā)生泄漏输枯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一占贫、第九天 我趴在偏房一處隱蔽的房頂上張望桃熄。 院中可真熱鬧,春花似錦型奥、人聲如沸瞳收。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽螟深。三九已至,卻和暖如春坑匠,著一層夾襖步出監(jiān)牢的瞬間血崭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工厘灼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留夹纫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓设凹,卻偏偏與公主長得像舰讹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子闪朱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353