Tomcat原理掃盲

Tomcat加載類和資源的順序爲:

  1. /Web-INF/classes
  2. /Web-INF/lib/*.jar
  3. Bootstrap
  4. System
  5. $CATALINA_HOME/common/classes
  6. $CATALINA_HOME/common/endores/*.jar
  7. $CATALINA_HOME/common/lib/*.jar
  8. $CATALINA_HOME/shared/classes
  9. $CATALINA_HOME/shared/lib/*.jar

Tomcat核心組件Connecter和Container

1.Connecter

1.1Connector的功能

  • 一個Connecter將在某個指定的端口上偵聽客戶請求,接收瀏覽器的發過來的 tcp 連接請求,創建一個 Request 和 Response 對象分別用於和請求端交換數據,然後會產生一個線程來處理這個請求並把產生的 Request 和 Response 對象傳給處理Engine(Container中的一部分),從Engine出獲得響應並返回客戶。
  • Tomcat中有兩個經典的Connector,一個直接偵聽來自Browser的HTTP請求,另外一個來自其他的WebServer請求。Cotote HTTP/1.1 Connector在端口8080處偵聽來自客戶Browser的HTTP請求,Coyote JK2 Connector在端口8009處偵聽其他Web Server的Servlet/JSP請求。
  • Connector 最重要的功能就是接收連接請求然後分配線程讓 Container 來處理這個請求,所以這必然是多線程的,多線程的處理Connector 設計的核心。

1.2Connector的protocol

  • protocol負責接收HTTP請求,Tomcat中支持兩種協議的連接器:HTTP/1.1與AJP/1.3,默認端口8080,該兩種協議有三種不同的實現方式
    :JIO(java.io—>BIO阻塞隊列)、APR、NIO
  • protocol配置
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="utf-8"/>
  • BIO實現的Connector中 接收請求的JIoEndpoint對象。JIoEndpoint維護了Acceptor和Worker:Acceptor接收socket,然後從Worker線程池中找出空閒的線程處理socket,如果worker線程池沒有空閒線程,則Acceptor將阻塞。其中Worker是Tomcat自帶的線程池,如果通過配置了其他線程池,原理與Worker類似。

  • 在NIO實現的Connector中,處理請求的主要實體是NIoEndpoint對象。NIoEndpoint中除了包含Acceptor和Worker外,還使用了Poller,Acceptor接收socket後,不是直接使用Worker中的線程處理請求,而是先將請求發送給了Poller,而Poller是實現NIO的關鍵。Acceptor向Poller發送請求通過隊列實現,使用了典型的生產者-消費者模式。在Poller中,維護了一個Selector對象;當Poller從隊列中取出socket後,註冊到該Selector中;然後通過遍歷Selector,找出其中可讀的socket,並使用Worker中的線程處理相應請求。與BIO類似,Worker也可以被自定義的線程池代替。
    通過上述過程可以看出,在NIoEndpoint處理請求的過程中,無論是Acceptor接收socket,還是線程處理請求,使用的仍然是阻塞方式;但在“讀取socket並交給Worker中的線程”的這個過程中,使用非阻塞的NIO實現,這是NIO模式與BIO模式的最主要區別(其他區別對性能影響較小,暫時略去不提)。而這個區別,在併發量較大的情形下可以帶來Tomcat效率的顯著提升:

    目前大多數HTTP請求使用的是長連接(HTTP/1.1默認keep-alive爲true),而長連接意味着,一個TCP的socket在當前請求結束後,如果沒有新的請求到來,socket不會立馬釋放,而是等timeout後再釋放。如果使用BIO,“讀取socket並交給Worker中的線程”這個過程是阻塞的,也就意味着在socket等待下一個請求或等待釋放的過程中,處理這個socket的工作線程會一直被佔用,無法釋放;因此Tomcat可以同時處理的socket數目不能超過最大線程數,性能受到了極大限制。而使用NIO,“讀取socket並交給Worker中的線程”這個過程是非阻塞的,當socket在等待下一個請求或等待釋放時,並不會佔用工作線程,因此Tomcat可以同時處理的socket數目遠大於最大線程數,併發性能大大提高。
    在這裏插入圖片描述

1.3Connector的關鍵參數

  • 【acceptCount】: 接收隊列的長度,隊列滿時,拒絕接收請求,acceptCount設置過大會導致請求等待時間長,設置過小,會立馬返回拒絕鏈接問題
  • 【maxConnections】:最大連接數,Tomcat任意時刻接收和處理的最大連接數。當Tomcat接收的連接數達到maxConnections時,Acceptor線程不會讀取accept隊列中的連接;這時accept隊列中的線程會一直阻塞着,直到Tomcat接收的連接數小於maxConnections。
  • 【maxThreads】:Tomcat的最大線程數,使用BIO場景,maxConnections和maxThreads應該一致,使用NIO場景,maxConnections可以遠大於maxThreads

Container

Container的體系結構

在這裏插入圖片描述

  • Engine 容器
    Engine 容器比較簡單,它只定義了一些基本的關聯關係
    Host 容器
  • Host 是 Engine 的字容器,一個 Host 在 Engine 中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這些應用,並且標識這個應用以便能夠區分它們。它的子容器通常是 Context,它除了關聯子容器外,還有就是保存一個主機應該有的信息。
  • Context 容器
    Context 代表 Servlet 的 Context,它具備了 Servlet 運行的基本環境,理論上只要有 Context 就能運行 Servlet 了。簡單的 Tomcat 可以沒有 Engine 和 Host。Context 最重要的功能就是管理它裏面的 Servlet 實例,Servlet 實例在 Context 中是以 Wrapper 出現的,還有一點就是 Context 如何才能找到正確的 Servlet 來執行它呢? Tomcat5 以前是通過一個 Mapper 類來管理的,Tomcat5 以後這個功能被移到了 request 中,在前面的時序圖中就可以發現獲取子容器都是通過 request 來分配的。
  • Wrapper 容器
    Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器,它沒有子容器了,所以調用它的 addChild 將會報錯。
    Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種信息打交道。

Tomcat處理HTTP請求的過程

在這裏插入圖片描述

  1. 用戶點擊網頁內容,請求被髮送到本機端口8080,被在那裏監聽的Coyote HTTP/1.1 Connector獲得。
  2. Connector把該請求交給它所在的Service的Engine來處理,並等待Engine的迴應。
  3. Engine獲得請求localhost/test/index.jsp,匹配所有的虛擬主機Host。
  4. Engine匹配到名爲localhost的Host(即使匹配不到也把請求交給該Host處理,因爲該Host被定義爲該Engine的默認主機),名爲localhost的Host獲得請求/test/index.jsp,匹配它所擁有的所有的Context。Host匹配到路徑爲/test的Context(如果匹配不到就把該請求交給路徑名爲“ ”的Context去處理)。
  5. path=“/test”的Context獲得請求/index.jsp,在它的mapping table中尋找出對應的Servlet。Context匹配到URL PATTERN爲*.jsp的Servlet,對應於JspServlet類。
  6. 構造HttpServletRequest對象和HttpServletResponse對象,作爲參數調用JspServlet的doGet()或doPost().執行業務邏輯、數據存儲等程序。
  7. Context把執行完之後的HttpServletResponse對象返回給Host。
  8. Host把HttpServletResponse對象返回給Engine。
  9. Engine把HttpServletResponse對象返回Connector。
  10. Connector把HttpServletResponse對象返回給客戶Browser。
    【Tomcat一個線程處理HTTP請求的調用棧】
    在這裏插入圖片描述
  • 從調用棧看Tomcat主體流程:Connector讀取Socket請求–>交給container容器(Engine->Host->Context)->Dofilter->Servlet分發請求對應到處理請求的實體類

Tomcat的Filter

通過Filter技術,開發人員可以實現用戶在訪問某個目標資源之前,對訪問的請求和響應進行攔截。簡單說,就是可以實現web容器對某資源的訪問前截獲進行相關的處理,還可以在某資源向web容器返回響應前進行截獲進行處理。

  • Filter的工作流程
    在這裏插入圖片描述
  • Filter採用的是責任鏈設計模式
  • Filter的執行邏輯:
  1. 把要執行的servlet存放到過濾器鏈中。
  2. 如果沒有配置過濾器則return一個空的過濾器鏈(只包含上面設置的servlet)。
  3. 如果配置過濾器,則把所有配置的過濾器加入到過濾器鏈中
    3.1 首先判斷filter-mapping中配置的dispatcher規則,如果符合則進入下一步
    3.2 然後判斷filter-mapping中配置的url-pattern規則,如果符合則添加到過濾器鏈
  • Tomcat過濾器的順序是按照web.xml中的先後順序執行的
  • Tomcat的 filter配置,init-param可以用作排除過濾器請求的聲明
    <filter>
        <filter-name>fiter</filter-name>
        <filter-class>*****</filter-class>
        <init-param>
            <param-name>excludedPages</param-name>
            <param-value>/rest/.*</param-value><!-- 匹配不做攔截的請求聲明-->
        </init-param>	
    </filter>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章