1. 程式人生 > >Tomcat詳解系列(2) - 理解Tomcat架構設計

Tomcat詳解系列(2) - 理解Tomcat架構設計

# Tomcat - 理解Tomcat架構設計 > 前文我們已經介紹了一個簡單的Servlet容器是如何設計出來,我們就可以開始正式學習Tomcat了,在學習開始,我們有必要站在高點去看看Tomcat的架構設計。@pdai ## 準備知識 > 一些準備知識點。 ### Tomcat和Catalina是什麼關係? > Tomcat的前身為Catalina,Catalina又是一個輕量級的Servlet容器 Tomcat的前身為Catalina,Catalina又是一個輕量級的Servlet容器。在美國,catalina是一個很美的小島。所以Tomcat作者的寓意可能是想把Tomcat設計成一個優雅美麗且輕量級的web伺服器。Tomcat從4.x版本開始除了作為支援Servlet的容器外,額外加入了很多的功能,比如:jsp、el、naming等等,所以說**Tomcat不僅僅是Catalina**。 ### 什麼是Servlet? > 所謂Servlet,其實就是Sun為了讓Java能實現動態可互動的網頁,從而進入Web程式設計領域而制定的一套標準! 在網際網路興起之初,當時的Sun公司(後面被Oracle收購)已然看到了這次機遇,於是設計出了Applet來對Web應用的支援。不過事實卻並不是預期那麼得好,Sun悲催地發現Applet並沒有給業界帶來多大的影響。經過反思,Sun就想既然機遇出現了,市場前景也非常不錯,總不能白白放棄了呀,怎麼辦呢?於是又投入精力去搞一套規範出來,這時Servlet誕生了! 一個Servlet主要做下面三件事情: + 建立並填充Request物件,包括:URI、引數、method、請求頭資訊、請求體資訊等 + 建立Response物件 + 執行業務邏輯,將結果通過Response的輸出流輸出到客戶端 **Servlet沒有main方法,所以,如果要執行,則需要在一個容器裡面才能執行,這個容器就是為了支援Servlet的功能而存在,Tomcat其實就是一個Servlet容器的實現**。 ## Tomcat 總結架構 下圖應該是網上能找的最好的關於Tomcat的架構圖了, 我們來看下它的構成: ![](https://pdai.tech/_images/tomcat/tomcat-x-design-2-1.jpeg) ### 從元件的角度看 + **Server**: 表示伺服器,它提供了一種優雅的方式來啟動和停止整個系統,不必單獨啟停聯結器和容器;它是Tomcat構成的頂級構成元素,所有一切均包含在Server中; + **Service**: 表示服務,Server可以執行多個服務。比如一個Tomcat裡面可執行訂單服務、支付服務、使用者服務等等;Server的實現類StandardServer可以包含一個到多個Services, Service的實現類為StandardService呼叫了容器(Container)介面,其實是呼叫了Servlet Engine(引擎),而且StandardService類中也指明瞭該Service歸屬的Server; + **Container**: 表示容器,可以看做Servlet容器;引擎(Engine)、主機(Host)、上下文(Context)和Wraper均繼承自Container介面,所以它們都是容器。 + Engine -- 引擎 + Host -- 主機 + Context -- 上下文 + Wrapper -- 包裝器 + **Connector**: 表示聯結器, **它將Service和Container連線起來**,首先它需要註冊到一個Service,它的作用就是把來自客戶端的請求轉發到Container(容器),這就是它為什麼稱作聯結器, 它支援的協議如下: + 支援AJP協議 + 支援Http協議 + 支援Https協議 + **Service內部**還有各種支撐元件,下面簡單羅列一下這些元件 + Manager -- 管理器,用於管理會話Session + Logger -- 日誌器,用於管理日誌 + Loader -- 載入器,和類載入有關,只會開放給Context所使用 + Pipeline -- 管道元件,配合Valve實現過濾器功能 + Valve -- 閥門元件,配合Pipeline實現過濾器功能 + Realm -- 認證授權元件 ### 從web.xml配置和模組對應角度 > 上述模組的理解不是孤立的,它直接對映為Tomcat的web.xml配置,讓我們聯絡起來看 ![](https://pdai.tech/_images/tomcat/tomcat-x-design-2-3.jpg) ### 從一個完整請求的角度來看 > 通過一個完整的HTTP請求,我們還需要把它貫穿起來 假設來自客戶的請求為:http://localhost:8080/test/index.jsp 請求被髮送到本機埠8080,被在那裡偵聽的Coyote HTTP/1.1 Connector,然後 * Connector把該請求交給它所在的Service的Engine來處理,並等待Engine的迴應 * Engine獲得請求localhost:8080/test/index.jsp,匹配它所有虛擬主機Host * Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的預設主機) * localhost Host獲得請求/test/index.jsp,匹配它所擁有的所有Context * Host匹配到路徑為/test的Context(如果匹配不到就把該請求交給路徑名為""的Context去處理) * path="/test"的Context獲得請求/index.jsp,在它的mapping table中尋找對應的servlet * Context匹配到URL PATTERN為*.jsp的servlet,對應於JspServlet類,構造HttpServletRequest物件和HttpServletResponse物件,作為引數呼叫JspServlet的doGet或doPost方法 * Context把執行完了之後的HttpServletResponse物件返回給Host * Host把HttpServletResponse物件返回給Engine * Engine把HttpServletResponse物件返回給Connector * Connector把HttpServletResponse物件返回給客戶browser ### 從原始碼的設計角度看 > 從功能的角度將Tomcat原始碼分成5個子模組,分別是: + **Jsper模**: 這個子模組負責jsp頁面的解析、jsp屬性的驗證,同時也負責將jsp頁面動態轉換為java程式碼並編譯成class檔案。在Tomcat原始碼中,凡是屬於org.apache.jasper包及其子包中的原始碼都屬於這個子模組; + **Servlet和Jsp模組**: 這個子模組的原始碼屬於javax.servlet包及其子包,如我們非常熟悉的javax.servlet.Servlet介面、javax.servet.http.HttpServlet類及javax.servlet.jsp.HttpJspPage就位於這個子模組中; + **Catalina模組**: 這個子模組包含了所有以org.apache.catalina開頭的java原始碼。該子模組的任務是規範了Tomcat的總體架構,定義了Server、Service、Host、Connector、Context、Session及Cluster等關鍵元件及這些元件的實現,這個子模組大量運用了Composite設計模式。同時也規範了Catalina的啟動及停止等事件的執行流程。從程式碼閱讀的角度看,這個子模組應該是我們閱讀和學習的重點。 + **Connector模組**: 如果說上面三個子模組實現了Tomcat應用伺服器的話,那麼這個子模組就是Web伺服器的實現。所謂聯結器(Connector)就是一個連線客戶和應用伺服器的橋樑,它接收使用者的請求,並把使用者請求包裝成標準的Http請求(包含協議名稱,請求頭Head,請求方法是Get還是Post等等)。同時,這個子模組還按照標準的Http協議,負責給客戶端傳送響應頁面,比如在請求頁面未發現時,connector就會給客戶端瀏覽器傳送標準的Http 404錯誤響應頁面。 + **Resource模組**: 這個子模組包含一些資原始檔,如Server.xml及Web.xml配置檔案。嚴格說來,這個子模組不包含java原始碼,但是它還是Tomcat編譯執行所必需的。 ### 從後續深入理解的角度 > 我們看完上述元件結構後,後續應該重點從哪些角度深入理解Tomcat呢? + **基於元件的架構** 我們知道組成Tomcat的是各種各樣的元件,每個元件各司其職,元件與元件之間有明確的職責劃分,同時元件與元件之間又通過一定的聯絡相互通訊。Tomcat整體就是一個個元件的堆砌! + **基於JMX** 我們在後續閱讀Tomcat原始碼的時候,會發現程式碼裡充斥著大量的類似於下面的程式碼。 ```java Registry.getRegistry(null, null).invoke(mbeans, "init", false); Registry.getRegistry(null, null).invoke(mbeans, "start", false); ``` 而這實際上就是通過JMX來管理相應物件的程式碼。這兒我們不會詳細講述什麼是JMX,我們只是簡單地說明一下JMX的概念,參考JMX百度百科。 > JMX(Java Management Extensions,即Java管理擴充套件)是一個為應用程式、裝置、系統等植入管理功能的框架。JMX可以跨越一系列異構作業系統平臺、系統體系結構和網路傳輸協議,靈活的開發無縫整合的系統、網路和服務管理應用。 + **基於生命週期** 如果我們查閱各個元件的原始碼,會發現絕大多陣列件實現了Lifecycle介面,這也就是我們所說的基於生命週期。生命週期的各個階段的觸發又是基於事件的方式。 ## 更多文章 *相關文章* + [Tomcat - 如何設計一個簡單的web容器](https://pdai.tech/md/framework/tomcat-x-design-web-container.html) + 在學習Tomcat前,很多人先入為主的對它的認知是巨複雜的;所以第一步,在學習它之前,要打破這種觀念,我們通過學習如何設計一個最基本的web容器來看它需要考慮什麼;進而在真正學習Tomcat時,多把重點放在它的頂層設計上,而不是某一塊程式碼上, 思路永遠比具體實現重要的多。 + [Tomcat - 理解Tomcat架構設計](https://pdai.tech/md/framework/tomcat/tomcat-x-arch.html) + 前文我們已經介紹了一個簡單的Servlet容器是如何設計出來,我們就可以開始正式學習Tomcat了,在學習開始,我們有必要站在高點去看看Tomcat的架構設計。 + [Tomcat - 原始碼分析準備和分析入口](https://pdai.tech/md/framework/tomcat/tomcat-x-sourcecode.html) + 上文我們介紹了Tomcat的架構設計,接下來我們便可以下載原始碼以及尋找原始碼入口了。 + [Tomcat - 啟動過程:初始化和啟動流程](https://pdai.tech/md/framework/tomcat/tomcat-x-start.html) + 在有了Tomcat架構設計和原始碼入口以後,我們便可以開始真正讀原始碼了。 + [Tomcat - 啟動過程:類載入機制詳解](https://pdai.tech/md/framework/tomcat/tomcat-x-classloader.html) + 上文我們講了Tomcat在初始化時會初始化classLoader。本文將具體分析Tomcat的類載入機制,特別是區別於傳統的`雙親委派模型`的載入機制。 + [Tomcat - 啟動過程:Catalina的載入](https://pdai.tech/md/framework/tomcat/tomcat-x-catalina.html) + 通過前兩篇文章,我們知道了[Tomcat的類載入機制](https://pdai.tech/md/framework/tomcat/tomcat-x-classloader.html)和[整體的元件載入流程](https://pdai.tech/md/framework/tomcat/tomcat-x-start.html);我們也知道通過Bootstrap初始化的catalinaClassLoader載入了Catalina,那麼進而引入了一個問題就是Catalina是如何載入的呢?載入了什麼呢?本文將帶你進一步分析。 + [Tomcat - 元件生命週期管理:LifeCycle](https://pdai.tech/md/framework/tomcat/tomcat-x-lifecycle.html) + 上文中,我們已經知道Catalina初始化了Server(它呼叫了 Server 類的 init 和 start 方法來啟動 Tomcat);你會發現Server是Tomcat的配置檔案server.xml的頂層元素,那這個階段其實我們已經進入到Tomcat內部元件的詳解;這時候有一個問題,這麼多元件是如何管理它的生命週期的呢? + [Tomcat - 元件拓展管理:JMX和MBean](https://pdai.tech/md/framework/tomcat/tomcat-x-jmx.html) + 我們在前文中講Lifecycle以及元件,怎麼會突然講JMX和MBean呢?本文通過承接上文Lifecycle講Tomcat基於JMX的實現。 + [Tomcat - 事件的監聽機制:觀察者模式](https://pdai.tech/md/framework/tomcat/tomcat-x-listener.html) + 本文承接上文中Lifecycle中實現,引出Tomcat的監聽機制。 + [Tomcat - Server的設計和實現: StandardServer](https://pdai.tech/md/framework/tomcat/tomcat-x-server.html) + 基於前面的幾篇文章,我們終於可以總體上梳理Server的具體實現了,這裡體現在StandardServer具體的功能實現上。 + [Tomcat - Service的設計和實現: StandardService](https://pdai.tech/md/framework/tomcat/tomcat-x-service.html) + 上文講了Server的具體實現了,本文主要講Service的設計和實現;我們從上文其實已經知道Server中包含多個service了。 + [Tomcat - 執行緒池的設計與實現:StandardThreadExecutor](https://pdai.tech/md/framework/tomcat/tomcat-x-executor.html) + 上文中我們研究了下Service的設計和實現,StandardService中包含Executor的呼叫;這個比較好理解,Tomcat需要併發處理使用者的請求,自然而言就想到執行緒池,那麼Tomcat中執行緒池(Executor)具體是如何實現的?本文帶你繼續深度解析。 + [Tomcat - Request請求處理: Container設計](https://pdai.tech/md/framework/tomcat/tomcat-x-container.html) + 在理解了Server,Service和Executor後,我們可以進入Request處理環節了。我們知道客戶端是可以發起多個請求的,Tomcat也是可以支援多個webapp的,有多個上下文,且一個webapp中可以有多個Servlet...等等,那麼Tomcat是如何設計元件來支撐請求處理的呢?本節文將介紹Tomcat的Container設計。 + [Tomcat - Container容器之Engine:StandardEngine](https://pdai.tech/md/framework/tomcat/tomcat-x-container-engine.html) + 上文已經知道Container的整體結構和設計,其中Engine其實就是Servlet Engine,負責處理request的頂層容器。 + [Tomcat - Container的管道機制:責任鏈模式](https://pdai.tech/md/framework/tomcat/tomcat-x-container-pipline.html) + 上文中介紹了Engine的設計,其中有Pipline相關內容沒有介紹,本文將向你闡述Tomcat的管道機制以及它要解決的問題。 + [Tomcat - Request請求處理過程:Connector](https://pdai.tech/md/framework/tomcat/tomcat-x-connector.html) + 本文主要介紹request請求的處理