1. 程式人生 > 程式設計 >深入理解Tomcat架構

深入理解Tomcat架構

   Tomcat對於web開發人員來說再熟悉不過了,它是由Apache開發的一個免費開源的Web應用伺服器。在Web開發時,經常用它構建輕量級的Java Web服務。想要簡單的使用Tomcat是非常容易的,但是想要深入瞭解Tomcat體系必須要了解它背後的架構設計。

   本篇文章對《Tomcat核心設計剖析》這本書的閱讀總結,大概的梳理了一下Tomcat的架構設計、模組組成。

   其實從Tomcat配置檔案Server.xml的格式就能看出它的結構。

<?xml version='1.0' encoding='utf-8'?>
<Server>
	<Listener
/>
<GlobalNamingResource> <Resoource/> </GlobalNamingResource> <Service> <Executor/> <Connector/> <Engine> <Cluster/> <Realm/> <Host> <Context/> </Host
>
</Engine> </Service> </Server> 複製程式碼

Server元件

   Server元件代表整個Tomcat容器,是Tomcat最外層的元件,它包括三個部分:生命週期監聽器,全域性命名資源,Service元件

1.生命週期監聽器

   由於Tomcat是龐大而複雜的,並且它擁有很多元件,如果這些元件一個一個單獨啟動時非常麻煩的,而且容易遺漏,不易擴充套件其他元件。所以Tomcat提供了監聽器方案,在其生命週期的不同階段呼叫監聽器,如果某個元件對於某事件感興趣,只需要實現介面即可

2.全域性命名資源

   對應Server.xml配置檔案的節點就是<GlobalNamingResource>,在Tomcat初始化時通過Digester框架將其解析成物件,並以樹狀結構儲存,它提供的命名物件通過ResourceLink給所有Web應用訪問使用。如:JNDI中儲存資料庫連線池物件。

3.監聽關閉命令

   Server元件會開放一個埠用於監聽命令,預設為8005.當Tomcat介面收到“SHUTDOWN”命令時則會關閉程式。

Service元件

   Server元件中可以包含多個Service元件,Service元件也是Tomcat最外層的元件之一。包含若干Connector元件和Executor元件組合而成,其中不同的Connector元件可以使用不用的通訊協議,如:HTTP,AJP。

   Executor元件則是Service元件下的執行緒池,Connector元件可以通過Executor元件實現執行緒池共享,預設情況下使用自己的私有執行緒池,其他元件也可以使用。此外,Service元件還包含了一個非常重要的Engine元件,Connector元件負責接收客戶端訊息,而Engine元件則負責處理客戶端訊息。

Connector元件

   Connector主要職責就是接收客戶端連線並接收報文,將其解析後裔送給Engine處理。它的元件包括:Protocol元件,Mapper元件,CoyoteAdaptor元件。

   Protocol是協議的抽象,它有不同的實現,每一個實現對應一種通訊協議,其中包括的Endpoint是接收端的抽象,分為Acceptor專門接收客戶端連線的接收器元件和Executor執行緒池元件,Processor元件是對客戶端請求處理的抽象。

   Mapper是路由元件,它可以將請求分發到對應的Web應用的某個Servlet上。

   而CoyoteAdaptor組價是一個介面卡,它負責將Connector元件和Engine容器適配連線起來。

以Http協議來說:

   Protocol元件的HTTP例項則是Http11Protocol和HttpNIOProtocol。由IO模式的不同,可以分為阻塞模式和非阻塞模式

1.HTTP阻塞模式協議

   HttpProtocol表示阻塞式的HTTP協議通訊,由傳統Socket套接字為底層實現,它包含JIoEndpoint和Http11Processor元件

  • JIoEndpoint

   ServerSocketFactory根據不同的安全層次如HTTP,HTTPS建立ServerSocket物件供Acceptor使用。    連線數控制器(LimitLatch)對連線數進行計數,來一個請求加一,請求結束後減一。請求數過多時就阻塞請求或拒絕處理。    Socket接收器(Acceptor),監聽某個埠,當有連線時就將任務丟給任務執行器。    任務定義器(SocketProcessor),定義好任務處理的統一流程抽象,如:

{
	處理套接字輸出的響應報文;
	連線數計數器減一;
	關閉套接字;
}
複製程式碼

  任務執行器(Executor),維護一個任務佇列,不斷從任務佇列中取得任務,執行任務定義器定義好的任務。

  • HTTP阻塞處理器(Http11Processor)   Http11Processor提供了對Http協議的通訊處理,將接收到的報文解析成我們熟知的請求物件,Request和Response,提供一個門面物件供Web應用使用。

2.Http非阻塞模式協議

  Http11NIOProtocol表示非阻塞模式Http協議的通訊,它主要包含NioEndpoint元件和Http11NioProcessor元件。一個連線到來時,將被註冊到NioChannel佇列中,由Poller負責檢測通道的讀寫事件,並在建立任務後扔進執行緒池中。

  BIO模式在接收到一個請求時,將開啟一個執行緒處理該請求,此時一個執行緒只能處理一個請求,並且請求並不是like傳送資料,由於網路或業務的一些原因,服務端並不能立刻接收到資料進行處理,而在等待資料準備的過程中造成了資源浪費。

  NIO模式克服了這種弊端,它能基於時間在一個執行緒中同時維護大量連線,它將套接字工作交給一個執行緒,讀寫交給其他N個執行緒,在接收請求後,它將請求放入Channel佇列中,用輪詢器不斷檢測通道讀寫事件,如果資料準備好了,可讀後,則將該連線交由連線池讀寫處理。

  NIO模式,對資料的操作讀寫,即呼叫基礎的IO操作API依然是阻塞的,只有對網路IO才是非阻塞的,在等待資料時是非阻塞的。

  同步非阻塞:同步時對IO來說,非阻塞是處理方式。

Engine容器

  Engine即為全域性引擎容器,它的標準實現為StandardEngine。其中主要包括元件有Host、AccessLog、Pipeline、Cluster、Realm、LifecycleListener、Log元件。

AccessLog、Log元件,日誌框架

  Tomcat提供了統一的日誌介面Log,並提供了國際化元件,在每個Java包下面都會存在LocalStrings.properties的不同語言版本。並且以Java包為單位劃分範圍,每個類如果需要查詢訊息都到對應的java包下的properties檔案中查詢。

  還設計了客戶端訪問日誌記錄介面,並提供了不同的實現,對持久化方式的不同有FileAccessLog,JDBCAccessLog等,還可以自己實現訪問日誌元件。

Pipeline(管道與閥門)

  在複雜的大型系統中,存在某個物件或資料流需要進行繁雜的邏輯處理,可以採用管道模式,劃分出每個小模組互相獨立且各自負責一段邏輯處理。

  如Request、Response就是這種需要進行繁複加工處理的物件,採用管道在其中設定閥門處理邏輯。在Tomcat中有4個級別的容器分別是 Engine、Host、Context、Wrapper。請求物件將分別由這4個容器處理,在4個容器之間通過管道機制進行傳遞,請求物件先通過Engine管道,經由若干個閥門處理,最後由基礎閥門處理流轉到下一級通道,每一級管道都有一個基礎閥門。

Cluster(叢集)元件

  Tomcat提供了叢集功能,可以快速的佈置Web叢集應用,而叢集之間的一些通訊則由Cluster元件完成。Cluster元件主要功能是提供會話複製,上下文屬性複製和在叢集下的web應用部署,並且Cluster元件可分為兩個級別Engine和Host。

  在Cluster元件內部真正負責叢集間通訊的是Tribes元件,它維護著一個叢集記憶體活主機列表,當Cluster呼叫傳送訊息介面時,由Tribes元件將訊息傳送到叢集中的其他主機上。

Realm元件

  Realm域其實可以看成一個包含了使用者及密碼的資料庫,根據使用者角色的不同,限制使用者訪問應用的url或資源資訊。Realm域是為了統一web容器資源安全、管理,統一抽象重複認證工作方便web應用資源許可權管理開發而提供的一個概念,它支援Engine、Host、Context級別容器的共享。

LifecycleListener

  LifecycleListener即是容器生命週期狀態監聽器。

Host元件

  Host元件是Servlet引擎中虛擬主機的抽象,Engine中可以包含多個Host容器,而一個Host容器也可以包含若干個Context容器,Host容器包括的元件有Context容器、AccessLog、Pipeline、Cluster、Realm、HostConfig、Log元件

1.HostConfig

  Host作為虛擬主機容器,用於放置Context級別容器,而Context其實對應的就是web應用,每個Tomcat應用都有自己的配置,當Tomcat啟動時,必須把對應web屬性載入進Context中,如果在Tomcat啟動時載入配置,這樣對Context的配置修改不會立刻生效,必須重啟Tomcat。所以Tomcat採用監聽器的方式,當Tomcat啟動時觸發“START_EVENT”事件時執行web應用部署動作。

Context容器

  Context容器對應一個Web應用程式,它包含若干個Wrapper元件、Realm、AccessLog、ErrorPage、Manager、DirContext、安全認證元件、JarScanner、過濾器、NamingResource、Mapper、Pipeline、WebAppLoader、ApplicationContext、InstanceManager、ServletContainerInitializer和Listeners元件。

  • AccessLog、Pipeline、Cluster、Realm,已經介紹過。
  • ErrorPage(錯誤頁面):每個Context容器都擁有各自的錯誤頁面物件,它可以在Context標籤下進行配置。
  • Manager(會話管理器):用於Context容器下的會話管理,提供不同的實現,有基於記憶體的,基於檔案的持久化,基於JDBC等實現方案,此外針對叢集,還提供了叢集會話管理器,可以在叢集中同步共享會話。
  • DirContext(目錄上下文):屬於JNDI的標準介面,目的是採用一種簡單的方式訪問整個Web應用包含的檔案,由於web應用檔案格式不同提供了WARDirContext、FileDirContext兩種實現。
  • JarScanner:顧名思義,它是專門用於掃描Context對於的Web應用的jar包。
  • 過濾器:在web開發中,過濾器是經常使用的元件之一,它提供了為某個web應用的所有請求和響應做統一處理的功能。

Wrapper容器

  Wrapper容器對應的就是Servlet,它內部維持了一個Servlet物件或者一個Servlet物件池。一般來說一個Wrapper只有一個Servlet物件,所以所有處理執行緒都呼叫同一個Servlet物件,但某個Servlet實現了SingleThreadModel介面也允許多個物件存在。

  request,response物件由Context基礎閥門流轉到Wrapper容器管道後,經過Wrapper管道的多個閥門,最後進入基礎閥門,基礎閥門呼叫過濾鏈,最後呼叫Servlet介面的service方法,對於HttpServlet實現來說,在呼叫service方法後,HttpServlet在service方法內部對Method進行判斷,分別呼叫doGet、doPost等不同方法。

類載入器

  容器最重要的是對於資源的隔離,Tomcat採用自定義的類載入器完成了資源的隔離解決了以下四個問題:

  • 同一個Web伺服器內,各web專案之間的java類庫相互隔離
  • 同一個web伺服器內,各web專案可以共享java類庫
  • 伺服器不能收web專案影響,所以伺服器類庫應該與應用程式隔離
  • 支援熱過載

  使用java類載入器可以動態載入需要的類,沒有使用到的類不需要載入,可以節省程式執行記憶體。在java中我們用完全匹配類名來標識一個類,而在JVM中由完全匹配類名和一個類載入器的例項Id作為唯一標識。也就是說,同一個虛擬機器器可以有兩個包名、類名都相同的類,只要它們由兩個不同的類載入器載入。這種特徵為我們提供了隔離機制,只要對不同的web專案用不同的類載入器載入jar包就可以隔離開資源類庫。而在熱過載的時候,只需要重新建立一個類載入器替換舊的類載入器,而原來的類載入器會被gc回收,就可以不用重啟Tomcat而重新載入了web應用程式。

Jasper

  Jasper就是JSP解析引擎,Tomcat使用Jasper解析Jsp檔案,將之轉化成繼承HttpJspBase的類檔案,然後用Eclipse JDT java編譯器或Ant編譯器將java檔案編譯成位元組碼檔案,最後採用類載入器載入位元組碼檔案。這也是為什麼Tomcat第一次訪問jsp檔案比較慢的原因,它需要將jsp編譯成位元組碼檔案載入執行,在第一次載入完成後,Tomcat將它快取下來下次就可以直接使用,並且Tomcat會在後臺執行緒不斷檢測Jsp檔案與編譯後的位元組碼檔案的最後修改時間是否相同,如果不相同,則表示jsp檔案有改動,則需要重新編譯。

總結

  Tomcat是一個十分龐大的專案,其中涉及到方方面面的知識,我根據《Tomcat核心設計剖析》這本書的介紹從巨集觀上了解了Tomcat的總體設計,依然某些方面理解起來十分吃力,對於Tomcat架構的細節方面依然需要時間慢慢體會,深入挖掘。本文也是比較淺顯的做了一下讀書筆記,如果其中有些地方我理解錯誤,歡迎評論指出。

  大概回想了一下,關於Tomcat架構主要就是圍繞Connector和Container元件,一個是接收器,一個是處理容器,而Container元件在Tomcat中沒有顯示標明,它其實就是Engine容器。通過接收器監聽埠連線,當有請求到來時,接收器將獲取請求讀取請求報文解析成Request、Response物件,之後交給Engine引擎進行處理,經過一層層處理後,接收器再將Response物件解析成響應報文返回給客戶端。