學習Tomcat(四)之Engine和Host容器

御狐神發表於2021-09-23

在前面的文章中,我們介紹了Tomcat的聯結器Connector,聯結器會監聽指定的埠,並把接受到的訊息轉為HttpServletRequest和HttpServletResponse,交個Servlet容器處理。Tomcat的Servlet容器分為四種:Engin容器/Host容器/Context容器/Wrapper容器,這四個容器之間是父子關係,Engine容器包含Host容器,Host容器包含Context,Context包含Wrapper容器。本文會介紹Tomcat容器中的Engin容器和Host容器,在下一篇文章中會介紹Context容器和Wrapper容器。

Container的作用

Tomcat中的Container用於處理聯結器處理好的Request和Response。Tomcat中的四種容器都繼承自Container介面,其中Engin容器全域性只有一個,是Container對外提供處理Request和Response的入口。Host容器是Engin容器的子容器,一個Engin容器可以包含多個Host容器,每個Host容器代表一個虛擬主機(下文會詳細介紹)。Engin容器在收到請求之後,會按照虛擬主機的配置將請求對映到對應的Host容器之上。

Container包含關係

Container的結構

如下圖所示,Tomcat中的四種Container都有相同的結構,包含以下幾部分關鍵元件:請求處理閥門鏈PipeLine、基礎閥門BaseValve和日誌元件等。

  1. PipeLine:用於流式加工處理請求中的資訊,每個PipeLine中可以包含多個閥門Valve,每個Valve都有同樣的方法invoke(Request request,Response response)
    <Engine name="Catalina" defaultHost="localhost">
        <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
            <!-- 預設 Valve -->
            <Valve className="org.apache.catalina.valves.AccessLogValve"
                directory="logs"
                prefix="localhost_access_log"
                suffix=".txt"
                <!-- maxDays="5" -->
                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
            <!-- 自定義 valve -->
            <Valve className="org.apache.catalina.valves.WswAccessValve"/>
        </Host>
    </Engine>
  1. BaseValve:基礎閥門,和Piple中的閥門的介面相同方法:invoke(Request request,Response response),但是作用和Piple中的閥門不同,主要用於將請求傳遞到下一個容器或者對應的Servlet元件。
  2. 日誌記錄器和生命週期管理等組其它元件,不具體介紹。

Container容器結構

Engine容器

Engine容器結構

如上圖所示,每個Tomcat僅僅有一個Engin容器,Tomcat中的聯結器接受並解析訊息之後,會把訊息的轉給Engin容器,使用者可以給Engin容器的PipeLine新增各種自定義的Valve,Engin容器會將一一呼叫PipeLine中的Valve。Engin容器的BaseValve是StandardEngineValve,這個Valve會讀取Request中的Host資訊,然後把請求路由給對應的Host容器。

final class StandardEngineValve extends ValveBase {

    public StandardEngineValve() {
        super(true);
    }

    @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Ignore some code here.
        // Select the Host to be used for this Request
        Host host = request.getHost();
        
        // Ignore some code here.

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
}

Host容器

Host容器是Engine容器的子容器,每個Host容器都是一個虛擬主機,對應於不同的域名。http協議從1.1開始,支援在請求頭裡面新增Host欄位用來表示請求的域名。DNS域名解析的時候,可以將不同的域名解析到同一個ip或者主機。Engine容器的BaseValve會讀取Request中的Host,然後呼叫對應Host容器的PipeLine去處理訊息。

Host路由原理

什麼是虛擬主機

假如我們需要在一個tomcat裡面同時支援三個域名:

我們需要在server.xml檔案裡面的Engine標籤下面新增多個Host標籤,如下所示,其中name表示域名,appbase表示虛擬主機的目錄。當我們在瀏覽器輸入http://www.ramki.com之後,相應域名將請求到tomcat。tomcat通過讀取並搜尋server.xml,找到www.ramki.com對應的虛擬主機Host,然後就使用查詢到的Host來處理請求。

<Host name="www.ramki.com" appbase="ramki_webapps" />
<Host name="www.krishnan.com" appbase="krishnan_webapps" /> 
<Host name="www.blog.ramki.com" appbase="blog_webapps" /> 

在瀏覽器請求的時候,請求頭資訊如下,這兒我們重點關注Host header。

GET /appA/servletA/some-url HTTP/1.1 
Host: www.ramki.com 
Proxy-Connection: keep-alive 
User-Agent: Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Encoding: gzip,deflate,sdch 
Accept-Language: en-US,en;q=0.8 

Context容器

Tomcat中一個Host容器可以包含多個Context容器,通常情況下一個Context容器標識一個應用,對應於wabapp目錄下面的一個工程,在我的下一篇部落格中會詳細介紹Context容器。

我是御狐神,歡迎大家關注我的微信公眾號:wzm2zsd

qrcode_for_gh_83670e17bbd7_344-2021-09-04-10-55-16

本文最先發布至微信公眾號,版權所有,禁止轉載!

相關文章