1. 程式人生 > >Tomcat內部結構及請求原理(轉)

Tomcat內部結構及請求原理(轉)

周期 成了 authent 代碼 部分 min 它的 war ace

Tomcat

Tomcat是一個JSP/Servlet容器。其作為Servlet容器,有三種工作模式:獨立的Servlet容器、進程內的Servlet容器和進程外的Servlet容器。

Tomcat的組織結構

Tomcat是一個基於組件的服務器,它的構成組件都是可配置的,其中最外層的是Catalina servlet容器,其他組件按照一定的格式要求配置在這個頂層容器中。
Tomcat的各種組件都是在Tomcat安裝目錄下的/conf/server.xml文件中配置的。

Tomcat目錄

tomcat
  |---bin:存放啟動和關閉tomcat腳本

  |---conf:存放不同的配置文件(server.xml和web.xml);
  |---doc:存放Tomcat文檔;
  |---lib/japser/common:存放Tomcat運行需要的庫文件(JARS);
  |---logs:存放Tomcat執行時的LOG文件;
  |---src:存放Tomcat的源代碼;
  |---webapps:Tomcat的主要Web發布目錄(包括應用程序示例);
  |---work:存放jsp編譯後產生的class文件;

TomCat配置文件

  • server.xml: Tomcat的主配置文件,包含Service, Connector, Engine, Realm, Valve, Hosts主組件的相關配置信息;
  • web.xml:遵循Servlet規範標準的配置文件,用於配置servlet,並為所有的Web應用程序提供包括MIME映射等默認配置信息;
  • tomcat-user.xml:Realm認證時用到的相關角色、用戶和密碼等信息;Tomcat自帶的manager默認情況下會用到此文件;在Tomcat中添加/刪除用戶,為用戶  指定角色等將通過編輯此文件實現;
  • catalina.policy:Java相關的安全策略配置文件,在系統資源級別上提供訪問控制的能力;
  • catalina.properties:Tomcat內部package的定義及訪問相關控制,也包括對通過類裝載器裝載的內容的控制;Tomcat在啟動時會事先讀取此文件的相關設置;
  • logging.properties: Tomcat6通過自己內部實現的JAVA日誌記錄器來記錄操作相關的日誌,此文件即為日誌記錄器相關的配置信息,可以用來定義日誌記錄的組  件級別以及日誌文件的存在位置等;
  • context.xml:所有host的默認配置信息;

Tomcat架構及常用的組件:

技術分享

1、Server組件

如上面示例文件中定義的:

<Server port=”8005” shutdown=”SHUTDOWN”>

這會讓Tomcat6啟動一個server實例(即一個JVM),它監聽在8005端口以接收shutdown命令,使用 telnet 連接8005 端口可以直接執行 SHUTDOWN 命令來關閉 Tomcat。各Server的定義不能使用同一個端口,這意味著如果在同一個物理機上啟動了多個Server實例,必須配置它們使用不同的端口。這個端口的定義用於為管理員提供一個關閉此實例的便捷途徑,因此,管理員可以直接telnet至此端口使用SHUTDOWN命令關閉此實例。不過,基於安全角度的考慮,這通常不允許遠程進行。

Server的相關屬性:

className: 用於實現此Server容器的完全限定類的名稱,默認為org.apache.catalina.core.StandardServer;

port: 接收shutdown指令的端口,默認僅允許通過本機訪問,默認為8005;

shutdown:發往此Server用於實現關閉tomcat實例的命令字符串,默認為SHUTDOWN;

2、Service組件:

Service主要用於關聯一個引擎和與此引擎相關的連接器,每個連接器通過一個特定的端口和協議接收入站請求交將其轉發至關聯的引擎進行處理。困此,Service要包含一個引擎、一個或多個連接器。

如上面示例中的定義:

<Service name=”Catalina”>

這定義了一個名為Catalina的Service,此名字也會在產生相關的日誌信息時記錄在日誌文件當中。

Service相關的屬性:

className: 用於實現service的類名,一般都是org.apache.catalina.core.StandardService。

name:此服務的名稱,默認為Catalina;

3、Connector組件:

進入Tomcat的請求可以根據Tomcat的工作模式分為如下兩類:

Tomcat作為應用程序服務器:請求來自於前端的web服務器,這可能是Apache, IIS, Nginx等;

Tomcat作為獨立服務器:請求來自於web瀏覽器;

Tomcat應該考慮工作情形並為相應情形下的請求分別定義好需要的連接器才能正確接收來自於客戶端的請求。一個引擎可以有一個或多個連接器,以適應多種請求方式。

定義連接器可以使用多種屬性,有些屬性也只適用於某特定的連接器類型。一般說來,常見於server.xml中的連接器類型通常有4種:

1) HTTP連接器 2) SSL連接器 3) AJP 1.3連接器 4) proxy連接器

如上面示例server.xml中定義的HTTP連接器:

<Connector port=”8080″ protocol=”HTTP/1.1″
maxThreads=”150″ connectionTimeout=”20000″
redirectPort=”8443″/>

定義連接器時可以配置的屬性非常多,但通常定義HTTP連接器時必須定義的屬性只有“port“,定義AJP連接器時必須定義的屬性只有”protocol”,因為默認的協議為HTTP。以下為常用屬性的說明:

下面是一個定義了多個屬性的SSL連接器:

<Connector port=”8443″
maxThreads=”150″ minSpareThreads=”25″ maxSpareThreads=”75″
enableLookups=”false” acceptCount=”100″ debug=”0″ scheme=”https” secure=”true”
clientAuth=”false” sslProtocol=”TLS” />

4、Engine組件:

Engine是Servlet處理器的一個實例,即servlet引擎,默認為定義在server.xml中的Catalina。Engine需要defaultHost屬性來為其定義一個接收所有發往非明確定義虛擬主機的請求的host組件。如前面示例中定義的:

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

常用的屬性定義:

defaultHost:Tomcat支持基於FQDN的虛擬主機,這些虛擬主機可以通過在Engine容器中定義多個不同的Host組件來實現;但如果此引擎的連接器收到一個發往非非明確定義虛擬主機的請求時則需要將此請求發往一個默認的虛擬主機進行處理,因此,在Engine中定義的多個虛擬主機的主機名稱中至少要有一個跟defaultHost定義的主機名稱同名;

name:Engine組件的名稱,用於日誌和錯誤信息記錄時區別不同的引擎;

Engine容器中可以包含Realm、Host、Listener和Valve子容器。

5、Host組件:

位於Engine容器中用於接收請求並進行相應處理的主機或虛擬主機,如前面示例中的定義:

<Host name=”localhost” appBase=”webapps”
unpackWARs=”true” autoDeploy=”true”
xmlValidation=”false” xmlNamespaceAware=”false”>
</Host>

常用屬性說明:

虛擬主機定義示例:

<Engine name=”Catalina” defaultHost=”localhost”>
<Host name=”localhost” appBase=”webapps”>
<Context path=”” docBase=”ROOT”/>
<Context path=”/bbs” docBase=”/web/bss”  #path路徑是定義在defaultHost背後的
reloadable=”true” crossContext=”true”/>
</Host>

<Host name=”mail.magedu.com” appBase=”/web/mail”>
<Context path=”” docBase=”ROOT”/>
</Host>
</Engine>

主機別名定義:

如果一個主機有兩個或兩個以上的主機名,額外的名稱均可以以別名的形式進行定義,如下:

<Host name=”www.ttlsa.com” appBase=”webapps” unpackWARs=”true”>
<Alias>feiyu.com</Alias>
</Host>

6、Context組件:

Context在某些意義上類似於apache中的路徑別名,一個Context定義用於標識tomcat實例中的一個Web應用程序;如下面的定義:

<!– Tomcat Root Context –>
<Context path=”” docBase=”/web/webapps”/>

<!– buzzin webapp –>
<Context path=”/bbs”
docBase=”/web/threads/bbs”
reloadable=”true”>
</Context>

<!– chat server –>
<Context path=”/chat” docBase=”/web/chat”/>

<!– darian web –>
<Context path=”/darian” docBase=”darian”/>

在Tomcat6中,每一個context定義也可以使用一個單獨的XML文件進行,其文件的目錄為$CATALINA_HOME/conf//。可以用於Context中的XML元素有Loader,Manager,Realm,Resources和WatchedResource。

常用的屬性定義有:

7、Realm組件:

一個Realm表示一個安全上下文,它是一個授權訪問某個給定Context的用戶列表和某用戶所允許切換的角色相關定義的列表。因此,Realm就像是一個用戶和組相關的數據庫。定義Realm時惟一必須要提供的屬性是classname,它是Realm的多個不同實現,用於表示此Realm認證的用戶及角色等認證信息的存放位置。

8、Valve組件:

Valve類似於過濾器,它可以工作於Engine和Host/Context之間、Host和Context之間以及Context和Web應用程序的某資源之間。一個容器內可以建立多個Valve,而且Valve定義的次序也決定了它們生效的次序。Tomcat6中實現了多種不同的Valve:

下面是一個常見的使用UserDatabase的配置:

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

下面是一個使用JDBC方式獲取用戶認證信息的配置:

<Realm className=”org.apache.catalina.realm.JDBCRealm” debug=”99″
driverName=”org.gjt.mm.mysql.Driver”
connectionURL=”jdbc:mysql://localhost/authority”
connectionName=”test” connectionPassword=”test”
userTable=”users” userNameCol=”user_name”
userCredCol=”user_pass”
userRoleTable=”user_roles” roleNameCol=”role_name” />

8、Valve組件:

Valve類似於過濾器,它可以工作於Engine和Host/Context之間、Host和Context之間以及Context和Web應用程序的某資源之間。一個容器內可以建立多個Valve,而且Valve定義的次序也決定了它們生效的次序。Tomcat6中實現了多種不同的Valve:

RemoteHostValve和RemoteAddrValve可以分別用來實現基於主機名稱和基於IP地址的訪問控制,控制本身可以通過allow或deny來進行定義,這有點類似於Apache的訪問控制功能;如下面的Valve則實現了僅允許本機訪問/probe:

<Context path=”/probe” docBase=”probe”>
<Valve className=”org.apache.catalina.valves.RemoteAddrValve”
allow=”127\.0\.0\.1″/>
</Context>

由Server.xml的結構看Tomcat的體系結構

<Server>                                                //頂層類元素,可以包括多個Service   
    <Service>                                           //頂層類元素,可包含一個Engine,多個Connecter
        <Connector>                                     //連接器類元素,代表通信接口
                <Engine>                                //容器類元素,為特定的Service組件處理客戶請求,要包含多個Host
                        <Host>                          //容器類元素,為特定的虛擬主機組件處理客戶請求,可包含多個Context
                                <Context>               //容器類元素,為特定的Web應用處理所有的客戶請求
                                </Context>
                        </Host>
                </Engine>
        </Connector>
    </Service>
</Server>

server.xml源碼如下

技術分享
<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->


    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
    -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
         This connector uses the NIO implementation. The default
         SSLImplementation will depend on the presence of the APR/native
         library and the useOpenSSL attribute of the
         AprLifecycleListener.
         Either JSSE or OpenSSL style configuration may be used regardless of
         the SSLImplementation selected. JSSE style configuration is used below.
    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->
    <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
         This connector uses the APR/native implementation which always uses
         OpenSSL for TLS.
         Either JSSE or OpenSSL style configuration may be used. OpenSSL style
         configuration is used below.
    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
               maxThreads="150" SSLEnabled="true" >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                         certificateFile="conf/localhost-rsa-cert.pem"
                         certificateChainFile="conf/localhost-rsa-chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

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

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>
View Code

由上可得出Tomcat的體系結構:
技術分享
             圖一:Tomcat的體系結構

由上圖可看出Tomca的心臟是兩個組件:Connecter和Container。一個Container可以選擇多個Connecter,多個Connector和一個Container就形成了一個Service。Service可以對外提供服務,而Server服務器控制整個Tomcat的生命周期。

    • 組件的生命線“Lifecycle”

      Service 和 Server 管理它下面組件的生命周期。
      Tomcat 中組件的生命周期是通過 Lifecycle 接口來控制的,組件只要繼承這個接口並實現其中的方法就可以統一被擁有它的組件控制了,這樣一層一層的直到一個最高級的組件就可以控制 Tomcat 中所有組件的生命周期,這個最高的組件就是 Server,而控制 Server 的是 Startup,也就是您啟動和關閉 Tomcat。

Tomca的兩大組件:Connecter和Container

Connecter組件

一個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 設計的核心。

Container組件

Container的體系結構如下:
技術分享
        圖二:Container的體系結構
Container是容器的父接口,該容器的設計用的是典型的責任鏈的設計模式,它由四個自容器組件構成,分別是Engine、Host、Context、Wrapper。這四個組件是負責關系,存在包含關系。通常一個Servlet class對應一個Wrapper,如果有多個Servlet定義多個Wrapper,如果有多個Wrapper就要定義一個更高的Container,如Context。
Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運行 war 程序,就必須要 Host,因為 war 中必有 web.xml 文件,這個文件的解析就需要 Host 了,如果要有多個 Host 就要定義一個 top 容器 Engine 了。而 Engine 沒有父容器了,一個 Engine 代表一個完整的 Servlet 引擎。

    • 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 中其它組件

Tomcat 還有其它重要的組件,如安全組件 security、logger 日誌組件、session、mbeans、naming 等其它組件。這些組件共同為 Connector 和 Container 提供必要的服務。

Tomcat Server處理一個HTTP請求的過程

技術分享
              圖三:Tomcat Server處理一個HTTP請求的過程

Tomcat Server處理一個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。

文章引用自:http://www.cnblogs.com/hggen/p/6264475.html

http://www.cnblogs.com/zhouyuqin/p/5143121.html

Tomcat內部結構及請求原理(轉)