Tomcat總體架構,啟動流程與處理請求流程

Cuzzz發表於2023-05-08

系列文章目錄和關於我

參考書籍《Tomcat架構解析》

一丶Tomcat總體架構

本文沿襲《Tomcat架構解析》中啟發式的方式來總結Tomcat總體架構

1 Server

假設當前我們要編寫一個web應用伺服器,web應用伺服器最基本的功能是接受客戶端傳送的請求資料並進行解析,完成相關的業務處理,然後將處理結果作為響應返回給請求計算機,於是我們定義了下面Server類

image-20230507180211525

從上面這段話來看,如果我們將上述功能全部讓Server去做實現,那麼讓請求監聽和請求處理耦合度很高,不利於擴充套件,比如監聽方式發生變化(比如之前使用BIO,現在使用NIO),或者請求處理方式改變(之前遵循Servlet規範,請求交給Servlet物件處理,現在適配另外一種規範)這種變化會導致我們在Server物件中修改程式碼,不滿足開閉原則,Server物件承受了太多,也不符合單一職責。

2.Connector 和 Container

如何解決請求監聽和請求處理耦合度很高的問題?加一層就好。

  • Connector

    Connector負責開啟socket並且監聽客戶端請求,返回響應資料。

  • Engine

    Engine負責處理具體的請求

如是我們有下面這種設計

image-20230507180552182

這種設計需要server維護Connector和Engine的關係,才可以將Connector的請求交給對應Engine

並且無法做到一個Tomcat伺服器執行多個服務,比如訂單服務,使用者服務。將Connector和Engine都交給Server處理,無法實現服務間的隔離。

3.Service

如何實現服務間的隔離?加一層就好

Server表示Tomcat伺服器,一個Tomcat伺服器可以部署多個服務,比如訂單服務,使用者服務。這裡的服務就是Service。

image-20230507180753214

由Service負責當前自己服務中的維護Connector 和 Engine,Server負責管理多個服務

4.Context,Host,Wrapper

  • Context

    應用伺服器是用來部署並允許web應用的,是一個執行環境,而不是一個獨立業務處理系統,因此在Engine容器需要支援管理web應用,這裡便是使用Context來表示一個web應用

  • Host

    Tomcat除了可以支援多個web應用之外,還需要可以支援多域名服務,比如一個主機可以承擔多個域名,比如newx.cuzz.com,game.cuzz,com。因此需要把每一個域名視為一個虛擬主機,在每一個虛擬主機下包含多個web應用。

  • Wrapper

    在一個web應用中可以包含多個不同的servlet例項處理來自不同連線的請求,因此還需要一個元件的概念來表示tomcat中的servlet,這個元件就是Wrapper

image-20230507181204958

5.Container

Container表示一類元件,它們負責接受來自客戶端的請求,並返回響應資料,這個過程往往會委託其他元件去完成,但是本質上它們是一致的——都是接受請求,返回響應資料。

Container表示容器,可以新增並維護子容器,因此Engine,Host,Context,Wrapper均繼承自Container

image-20230507181756809

Container還提供了backgroundProcess方法,方便子類實現後臺任務

6.Lifecycle

可以看到上圖中元件都存在start,stop等生命週期方法,因此Tomcat抽象出Lifecycle介面,表示生命週期,定義了init,start,stop,destory等生命週期回撥方法。並且還提供了LifecycleListener使用監聽器模式來實現生命週期事件監聽。

image-20230507182559565

7.Pipeline和Valve

為了增強擴充套件性,tomcat定義了Pipeline(管道)和Valve(閥),Pipeline使用職責鏈的方式串聯多個Valve——來自客戶端的請求如同流水一樣流淌在管道中,收到每一個閥的作用。

image-20230507184619665

Pipeline中維護了基礎的Valve,始終位於Pipeline末端,透過Pipeline#addValve新增的Valve違約基礎的Valve之前。

在Tomcat中Engine,Host,Context,Wrapper都有對應的Valve實現,同時維護了一個Pipeline,從而讓我們可以對請求的處理進行擴充套件。

image-20230507185116208

8.Connector設計

一個Connector處理請求的流程大致如下

image-20230507185822807

這些操作會被委託給Endpoint,ProtocolHandler,Processor

  • Endpoint:tomcat中沒有這個介面,只有AbstractEndpoint,它負責啟動執行緒來監聽伺服器埠,並且在接受到資料後交給Processor處理
  • Processor:Processor讀取到客戶端請求後按照請求地址對映到具體的容器進行處理,這個過程請求對映,Processor實現請求對映依賴於Mapper物件,在容器發生註冊和登出的時候,MapperListener會監聽到對應的事件,從而來變更Mapper中維護的請求對映資訊。
  • ProtocolHandler:協議處理器,針對不同的IO方式(NIO,BIO等)和不同的協議(Http,AJP)具備不同的實現,ProtocolHandler包含一個Endpoint來開啟埠監聽,並且包含一個Processor用於按照協議讀取資料並將請求交給容器處理。

image-20230507193102849

在次之外Tomcat 還有一個Adapter介面,上面說到Processor會依賴Mapper實現請求對映,但是其實Proccessor並沒有直接持有一個Mapper,而是持有一個Adapter,由Adapter負責實現請求對映並交由Container處理請求。Adapter在Tomcat中只有一個實現CoyoteAdapter。

CoyoteAdapter在Processor和 Mapper以及Container中橫插一腳,實現Connector和Mapper以及Container的解耦。

image-20230507202413947

結合tomcat整體架構後的圖

image-20230507203258251

9.Executor

Tomcat定義了Executor介面,只有一個實現類StandardThreadExecutor,目的是為了實現tomcat元件間的執行緒池共享,並且這個執行緒池由Service進行管理,即同一個Server中的元件可以共享一個執行緒池。

image-20230507203145137

10 Bootstrap與Catalina

Tomcat透過類Catalina提供了一個Shell程式,用於解析server.xml建立各個元件。同時,負責啟動、停止應用伺服器(啟動tomcat頂層元件Server)

Tomcat提供了Bootstrap作為應用伺服器啟動入口,Bootstrap負責反射建立Catalina例項,根據執行引數呼叫Catalina相關方法完成針對應用伺服器的操作(啟動、停止)。

在Tomcat釋出包中,Bootstrap位於$CATALINA_HOME/bin下,和Tomcat應用伺服器完全松耦合(透過反射呼叫Catalina例項),它可以直接依賴JRE執行併為Tomcat應用伺服器建立共享類載入器,用於構造Catalina例項及整個Tomcat伺服器。
image-20230507215430206
image-20230507213104299

二丶Tomcat啟動流程

image-20230507213047804

可以看到Tocmat的啟動流程非常標準化,這得益於這些元件都實現了Lifecyle介面。首先是呼叫init初始化元件,然後呼叫start方法啟動元件,每次呼叫都伴隨著生命週期事件的觸發。

三丶Tomcat處理一個請求的流程

應用程式的請求處理,開始於監聽伺服器socket埠接受到資料,結束與伺服器處理結果寫入Socket輸出流。

在此過程中,伺服器需要將請求內容按照協議內容進行解析,封裝為物件,然後根據請求對映規則定位到具體Servlet,這個Servlet中就是我們業務邏輯(SpringMVC中是DispatcherServlet將進一步將請求分發到Controller)Servlet處理結束後,響應物件將按照協議內容寫如輸出流。

image-20230507214918965

相關文章