天天看點

死磕Tomcat系列(1)——整體架構死磕Tomcat系列(1)——整體架構

死磕Tomcat系列(1)——整體架構

在許多的高端開發的崗位中都會或多或少有要求面試人員要研究過一些常用中間件源碼。這是因為一切的秘密都是藏在源碼中,閱讀源碼能夠讓我們對架構或者中間件的了解更加深刻,而我們也能夠在源碼的研究中獲得其中一些優秀的設計方式。而我們的中間件和源碼那麼多,我們該從何入手呢?其實大部分的中間件或者架構都有一些共性的部分,例如網絡程式設計、多線程、反射和類加載等技術。是以深入研究透了一兩個中間價的話,那麼再回過頭來看其他的中間件,那麼就會很容易了解它裡面所用的技術以及原理。而作為一個老牌的WEB端架構Tomcat,無論是其整體的架構設計,還是其内在的一些技術靈活應用,都值得我們一看。

在學習架構的時候,我一般都是對這個架構有一個整體的認識。知道它整體是如何運作的,然後再深入其中某部分進行研究,這樣會事半功倍。

整體架構

我們想要了解一個架構,首先要了解它是幹什麼的,Tomcat我們都知道,是用于處理連接配接過來的Socket請求的。那麼Tomcat就會有兩個功能:

  • 對外處理連接配接,将收到的位元組流轉化為自己想要的Request和Response對象
  • 對内處理Servlet,将對應的Request請求分發到相應的Servlet中

那麼我們整體的骨架就出來了,Tomcat其實就分為兩大部分,一部分是連接配接器(Connnector)處理對外連接配接和容器(Container)管理對内的Servelet。大體的關系圖如下

最外層的大框就是代表一個Tomcat服務,一個Tomcat服務可以對應多個Service。每個Service都有連接配接器和容器。這些對應的關系我們也可以打開在Tomcat目錄配置檔案中

server.xml

中看出來。

<Server port="8006" shutdown="SHUTDOWN">
  <Service name="Catalina">

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
       
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
       
      </Host>
    </Engine>
  </Service>
</Server>
           

這裡我将其中配置檔案中删除了一些内容精簡了一下,這裡我們可以看到連接配接器其實就是

Connector

,一個Service中可以有多個連接配接器,容器其實對應的就是

Engine

Tomcat的整體架構簡單來說就是這樣的對應關系。接下來我們簡單的介紹連接配接器的整體架構和容器的整體架構。

連接配接器

我們可以看到上圖中連接配接器傳給容器的是

ServletRequest

對象,而容器傳給連接配接器的是

ServletResponse

對象,這些在網絡傳輸過程中是肯定不行的,因為網絡傳輸中傳送的位元組流。是以連接配接器的功能需求我們大概能總結出來以下幾點。

  • Socket連接配接
  • 讀取請求網絡中的位元組流
  • 根據相應的協定(Http/AJP)解析位元組流,生成統一的

    Tomcat Request

    t對象
  • Tomcat Reques

    傳給容器
  • 容器傳回

    Tomcat Response

    對象
  • Tomcat Response

    對象轉換為位元組流
  • 将位元組流傳回給用戶端

其實上面的細分都能總結為以下的三點

  • 網絡通信
  • 應用層協定的解析
  • Tomcat的

    Request/Response

    ServletRequest/ServletResponse

    對象的轉化

而在Tomcat中它也用了三個類來實作上面的三個功能,分别對應如下

  • EndPoint
  • Processor
  • Adapter

用圖表示他們的關系的話就是這樣

容器

容器,顧名思義就是裝東西的器具,那麼這個Tomcat容器是裝什麼的呢?其實主要的就是裝了Servlet的。那麼容器是如何設計的呢?Tomcat的容器設計其實是用了組合設計模式(不了解組合設計模式的可以看我之前的文章

不學無數——組合模式

)。其實從

Server.xml

中我們也能看到其關系了。

<Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

      </Host>
    </Engine>
           

在這裡面我們隻能看到容器中的兩個子產品,一個是頂層子產品

Engine

,另一個是

Host

,其實還有兩個子產品,一個是

Context

對應的是我們webapp裡面的每個應用檔案夾,每個檔案夾就是對應一個

Context

,還有一個子產品

Wrapper

對應的是我們

Context

中的所有servlet,

Wrapper

管理了通路關系與具體的Servlet的對應。圖表示就是下面這樣。

Tomcat中容器所有子產品都實作了

Container

接口,而組合模式的意義就是使得使用者對于單個對象群組合對象的使用具有一緻性,即無論添加多少個

Context

其使用就是為了找到其下面的Servlet,而無論添加多少個Host也是為了找個下面的Servlet。而在容器中設計了這麼多的子產品,一個請求過來Tomcat如何找到對應的Servlet進行處理呢?

請求如何定位

我們就舉個最簡單的例子,我們本機應用上啟動了一個Tomcat,webapp下有我們部署的一個應用

buxuewushu

。我們在浏覽器上輸入

http://localhost:8080/buxuewushu/add.do

是如何找到對應Servlet進行處理呢?

在我們啟動Tomcat的時候,連接配接器就會進行初始化監聽所配置的端口号,這裡我們配置的是8080端口對應的協定是HTTP。

  • 請求發送到本機的8080端口,被在那裡監聽的HTTP/1.1的連接配接器Connector獲得
  • 連接配接器Connector将位元組流轉換為容器所需要的

    ServletRequest

    對象給同級

    Service

    下的容器子產品Engine進行處理
  • Engine獲得位址

    http://localhost:8080/buxuewushu/add

    。比對他下面的Host主機
  • 比對到名為localhost的Host(就算此時請求為具體的ip,沒有配置相應的Host,也會交給名為localhost的Host進行處理,因為他是預設的主機)
  • Host比對到路徑為

    /buxuewushu

    的Context,即在webapp下面找到相應的檔案夾
  • Context比對到URL規則為*.do的servlet,對應為某個Servlet類
  • 調用其

    doGet

    或者

    doPost

    方法
  • Servlet執行完以後将對象傳回給Context
  • Context傳回給Host
  • Host傳回給Engine
  • Engine傳回給連接配接器Connector
  • 連接配接器Connector将對象解析為位元組流發送給用戶端

往期關于Tomcat的文章

參考

  • [深入拆解Tomcat&Jetty]()

繼續閱讀