參考書籍《Tomcat架構解析》
本文沿襲《Tomcat架構解析》中啟發式的方式來總結Tomcat總體架構
假設當前我們要編寫一個web應用伺服器,web應用伺服器最基本的功能是接受使用者端傳送的請求資料並進行解析,完成相關的業務處理,然後將處理結果作為響應返回給請求計算機,於是我們定義了下面Server類
從上面這段話來看,如果我們將上述功能全部讓Server去做實現,那麼讓請求監聽和請求處理耦合度很高,不利於擴充套件,比如監聽方式發生變化(比如之前使用BIO,現在使用NIO),或者請求處理方式改變(之前遵循Servlet規範,請求交給Servlet物件處理,現在適配另外一種規範)這種變化會導致我們在Server物件中修改程式碼,不滿足開閉原則,Server物件承受了太多,也不符合單一職責。
如何解決請求監聽和請求處理耦合度很高的問題?加一層就好。
Connector
Connector負責開啟socket並且監聽使用者端請求,返回響應資料。
Engine
Engine負責處理具體的請求
如是我們有下面這種設計
這種設計需要server維護Connector和Engine的關係,才可以將Connector的請求交給對應Engine
並且無法做到一個Tomcat伺服器執行多個服務,比如訂單服務,使用者服務。將Connector和Engine都交給Server處理,無法實現服務間的隔離。
如何實現服務間的隔離?加一層就好
Server
表示Tomcat伺服器,一個Tomcat伺服器可以部署多個服務,比如訂單服務,使用者服務。這裡的服務就是Service。
由Service負責當前自己服務中的維護Connector 和 Engine,Server負責管理多個服務
Context
應用伺服器是用來部署並允許web應用的,是一個執行環境,而不是一個獨立業務處理系統,因此在Engine容器需要支援管理web應用,這裡便是使用Context來表示一個web應用
Host
Tomcat除了可以支援多個web應用之外,還需要可以支援多域名服務,比如一個主機可以承擔多個域名,比如newx.cuzz.com,game.cuzz,com。因此需要把每一個域名視為一個虛擬主機,在每一個虛擬主機下包含多個web應用。
Wrapper
在一個web應用中可以包含多個不同的servlet範例處理來自不同連線的請求,因此還需要一個元件的概念來表示tomcat中的servlet,這個元件就是Wrapper
Container表示一類元件,它們負責接受來自使用者端的請求,並返回響應資料,這個過程往往會委託其他元件去完成,但是本質上它們是一致的——都是接受請求,返回響應資料。
Container表示容器,可以新增並維護子容器,因此Engine,Host,Context,Wrapper均繼承自Container
Container還提供了backgroundProcess方法,方便子類實現後臺任務
可以看到上圖中元件都存在start,stop等生命週期方法,因此Tomcat抽象出Lifecycle介面,表示生命週期,定義了init,start,stop,destory等生命週期回撥方法。並且還提供了LifecycleListener使用監聽器模式來實現生命週期事件監聽。
為了增強擴充套件性,tomcat定義了Pipeline(管道)和Valve(閥),Pipeline使用職責鏈的方式串聯多個Valve——來自使用者端的請求如同流水一樣流淌在管道中,收到每一個閥的作用。
Pipeline中維護了基礎的Valve,始終位於Pipeline末端,通過Pipeline#addValve新增的Valve違約基礎的Valve之前。
在Tomcat中Engine,Host,Context,Wrapper都有對應的Valve實現,同時維護了一個Pipeline,從而讓我們可以對請求的處理進行擴充套件。
一個Connector處理請求的流程大致如下
這些操作會被委託給Endpoint,ProtocolHandler,Processor
在次之外Tomcat 還有一個Adapter介面,上面說到Processor會依賴Mapper實現請求對映,但是其實Proccessor並沒有直接持有一個Mapper,而是持有一個Adapter,由Adapter負責實現請求對映並交由Container處理請求。Adapter在Tomcat中只有一個實現CoyoteAdapter。
CoyoteAdapter在Processor和 Mapper以及Container中橫插一腳,實現Connector和Mapper以及Container的解耦。
結合tomcat整體架構後的圖
Tomcat定義了Executor介面,只有一個實現類StandardThreadExecutor,目的是為了實現tomcat元件間的執行緒池共用,並且這個執行緒池由Service進行管理,即同一個Server中的元件可以共用一個執行緒池。
Tomcat通過類Catalina提供了一個Shell程式,用於解析server.xml建立各個元件。同時,負責啟動、停止應用伺服器(啟動tomcat頂層元件Server)
Tomcat提供了Bootstrap作為應用伺服器啟動入口,Bootstrap負責反射建立Catalina範例,根據執行引數呼叫Catalina相關方法完成針對應用伺服器的操作(啟動、停止)。
在Tomcat釋出包中,Bootstrap位於$CATALINA_HOME/bin下,和Tomcat應用伺服器完全鬆耦合(通過反射呼叫Catalina範例),它可以直接依賴JRE執行併為Tomcat應用伺服器建立共用類載入器,用於構造Catalina範例及整個Tomcat伺服器。
可以看到Tocmat的啟動流程非常標準化,這得益於這些元件都實現了Lifecyle介面。首先是呼叫init初始化元件,然後呼叫start方法啟動元件,每次呼叫都伴隨著生命週期事件的觸發。
應用程式的請求處理,開始於監聽伺服器socket埠接受到資料,結束與伺服器處理結果寫入Socket輸出流。
在此過程中,伺服器需要將請求內容按照協定內容進行解析,封裝為物件,然後根據請求對映規則定位到具體Servlet,這個Servlet中就是我們業務邏輯(SpringMVC中是DispatcherServlet將進一步將請求分發到Controller)Servlet處理結束後,響應物件將按照協定內容寫如輸出流。