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的架構圖了, 我們來看下它的構成:
從組件的角度看
-
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配置,讓我們聯系起來看
從一個完整請求的角度來看
通過一個完整的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源碼的時候,會發現代碼里充斥着大量的類似於下面的代碼。
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容器
- 在學習Tomcat前,很多人先入為主的對它的認知是巨復雜的;所以第一步,在學習它之前,要打破這種觀念,我們通過學習如何設計一個最基本的web容器來看它需要考慮什么;進而在真正學習Tomcat時,多把重點放在它的頂層設計上,而不是某一塊代碼上, 思路永遠比具體實現重要的多。
- Tomcat - 理解Tomcat架構設計
- 前文我們已經介紹了一個簡單的Servlet容器是如何設計出來,我們就可以開始正式學習Tomcat了,在學習開始,我們有必要站在高點去看看Tomcat的架構設計。
- Tomcat - 源碼分析准備和分析入口
- 上文我們介紹了Tomcat的架構設計,接下來我們便可以下載源碼以及尋找源碼入口了。
- Tomcat - 啟動過程:初始化和啟動流程
- 在有了Tomcat架構設計和源碼入口以后,我們便可以開始真正讀源碼了。
- Tomcat - 啟動過程:類加載機制詳解
- 上文我們講了Tomcat在初始化時會初始化classLoader。本文將具體分析Tomcat的類加載機制,特別是區別於傳統的
雙親委派模型
的加載機制。
- 上文我們講了Tomcat在初始化時會初始化classLoader。本文將具體分析Tomcat的類加載機制,特別是區別於傳統的
- Tomcat - 啟動過程:Catalina的加載
- 通過前兩篇文章,我們知道了Tomcat的類加載機制和整體的組件加載流程;我們也知道通過Bootstrap初始化的catalinaClassLoader加載了Catalina,那么進而引入了一個問題就是Catalina是如何加載的呢?加載了什么呢?本文將帶你進一步分析。
- Tomcat - 組件生命周期管理:LifeCycle
- 上文中,我們已經知道Catalina初始化了Server(它調用了 Server 類的 init 和 start 方法來啟動 Tomcat);你會發現Server是Tomcat的配置文件server.xml的頂層元素,那這個階段其實我們已經進入到Tomcat內部組件的詳解;這時候有一個問題,這么多組件是如何管理它的生命周期的呢?
- Tomcat - 組件拓展管理:JMX和MBean
- 我們在前文中講Lifecycle以及組件,怎么會突然講JMX和MBean呢?本文通過承接上文Lifecycle講Tomcat基於JMX的實現。
- Tomcat - 事件的監聽機制:觀察者模式
- 本文承接上文中Lifecycle中實現,引出Tomcat的監聽機制。
- Tomcat - Server的設計和實現: StandardServer
- 基於前面的幾篇文章,我們終於可以總體上梳理Server的具體實現了,這里體現在StandardServer具體的功能實現上。
- Tomcat - Service的設計和實現: StandardService
- 上文講了Server的具體實現了,本文主要講Service的設計和實現;我們從上文其實已經知道Server中包含多個service了。
- Tomcat - 線程池的設計與實現:StandardThreadExecutor
- 上文中我們研究了下Service的設計和實現,StandardService中包含Executor的調用;這個比較好理解,Tomcat需要並發處理用戶的請求,自然而言就想到線程池,那么Tomcat中線程池(Executor)具體是如何實現的?本文帶你繼續深度解析。
- Tomcat - Request請求處理: Container設計
- 在理解了Server,Service和Executor后,我們可以進入Request處理環節了。我們知道客戶端是可以發起多個請求的,Tomcat也是可以支持多個webapp的,有多個上下文,且一個webapp中可以有多個Servlet...等等,那么Tomcat是如何設計組件來支撐請求處理的呢?本節文將介紹Tomcat的Container設計。
- Tomcat - Container容器之Engine:StandardEngine
- 上文已經知道Container的整體結構和設計,其中Engine其實就是Servlet Engine,負責處理request的頂層容器。
- Tomcat - Container的管道機制:責任鏈模式
- 上文中介紹了Engine的設計,其中有Pipline相關內容沒有介紹,本文將向你闡述Tomcat的管道機制以及它要解決的問題。
- Tomcat - Request請求處理過程:Connector
- 本文主要介紹request請求的處理過程。