1. 程式人生 > >Tomcat總體架構(Tomcat原始碼解析系列二)

Tomcat總體架構(Tomcat原始碼解析系列二)

         Tomcat即是一個HTTP伺服器,也是一個servlet容器,主要目的就是包裝servlet,並對請求響應相應的servlet,純servlet的web應用似乎很好理解Tomcat是如何裝載servlet的,但,當使用一些MVC框架時,如spring MVC、strusts2,可能就找不出servlet在哪裡?其實spring MVC框架就是一整個servlet,在web.xml中配置如下:

<!-- Spring MVC servlet -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<!-- 此處可以可以配置成*.do,對應struts的字尾習慣 -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
而Struts2是基於過濾器的(過濾器也稱作閥門,過濾器鏈相當於流水線),過濾器執行在呼叫servlet的service()方法之前。在web.xml的配置如下:
<span style="font-family:Comic Sans MS;font-size:18px;"><filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping></span>

他的總體結構用下圖來表示。


   通過上圖我們可以看出Tomcat中主要涉及Server,Service,Engine,Connector,Host,Context元件,之前用過Tomcat的童鞋是不是覺得這些元件的名稱有點似曾相識的趕腳,沒趕腳?!您再想想。好吧,不用你想了,我來告訴你吧。其實在Tomcat二進位制分發包解壓後,在conf目錄中有一個server.xml檔案,你開啟它瞄兩眼看看,是不是發現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">
  <!-- 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" />
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener" />
  <!-- 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 (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL 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 HTTP/1.1 Connector on port 8443
         This connector uses the JSSE configuration, when using APR, the
         connector should be using the OpenSSL style configuration
         described in the APR documentation -->
    <!--
    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />
    -->

    <!-- 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="192.168.1.50" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="192.168.1.50">

      <!--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="192.168.1.50"  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="192.168.1.50_access_log." suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

    Tomcat載入時相應元件(容器)的配置引數都是從這個檔案讀進去的,這個檔案也是Tomcat效能優化的關鍵。接下來我們就根據上圖以及conf/server.xml的內容來一步步描述一下上面所說的各種元件吧。

Server

Server是Tomcat中最頂層的元件,它可以包含多個Service元件。在Tomcat原始碼中Server元件對應原始碼中的  org.apache.catalina.core.StandardServer 類。StandardServer的繼承關係圖如下圖所示:


Lifecycle是Tomcat的生命週期介面。保持元件啟動和停止一致的的機制通過實現org.apache.catalina.Lifecycle介面來實現。

Service

接下來咋們來看看Service元件,Service元件相當於Connetor和Engine元件的包裝器,它將一個或者多個Connector元件和一個Engine建立關聯。上述配置檔案中,定義一個叫Catalina的服務,並將Http,AJP(定向包的協議)這兩個Connector關聯到了一個名為Catalina的Service,注意一個Connetor對應處理一種協議。Service元件對應Tomcat原始碼中的org.apache.catalina.core.StandardService,StandardService的繼承關係圖如下圖所示:


Connector

既然Tomcat需要提供http服務,而我們知道http應用層協議最終都是需要通過TCP層的協議進行傳遞的,而Connector正是Tomcat中監聽TCP網路連線的元件,一個Connector會監聽一個獨立的埠來處理來自客戶端的連線。預設的情況下Tomcat提供瞭如下兩個Connector。我們分別描述一下:

  1. HTTP/1.1
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> 上面定義了一個Connector,它預設監聽埠8080,這個埠我們可以根據具體情況進行改動。connectionTimeout定義了連線超時時間,單位是毫秒,redirectPort定義了ssl的重定向介面,根據預設的配置,Connector會將ssl請求重定向到8443埠。
  2. AJP/1.3
    AJP表示Apache Jserv Protocol,此聯結器將處理Tomcat和Aapache http伺服器之間的互動,這個聯結器是用來處理我們將Tomcat和Apache http伺服器結合使用的情況。假如在同樣的一臺物理Server上面部署了一臺Apache http伺服器和多臺Tomcat伺服器,通過Apache伺服器來處理靜態資源以及負載均衡的時候,針對不同的Tomcat例項需要AJP監聽不同的埠。
Connector對應原始碼中的org.apache.catalina.connector.Connector,它的繼承關係圖如下所示:


Engine

Tomcat中有一個容器的概念,而Engine,Host,Context都屬於Contanier,我們先來說說最頂層的容器Engine.
一個Engine可以包含一個或者多個Host,也就是說我們一個Tomcat的例項可以配置多個虛擬主機。
預設的情況下<Engine name="Catalina" defaultHost="localhost">定義了一個名稱為Cataline的Engine.Engine對應原始碼中的org.apache.catalina.core.StandardEngine,它的繼承關係圖如下圖所示:


Host定義了一個虛擬主機,一個虛擬主機可以有多個Context,預設的配置如下:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">….</Host> 其中appBase為webapps,也就是<CATALINA_HOME>\webapps目錄,unpackingWARS屬性指定在appBase指定的目錄中的war包都自動的解壓,預設配置為true,autoDeploy屬性指定是否對加入到appBase目錄的war包進行自動的部署,預設為true.
Host對應原始碼中的org.apache.catalina.core.StandardHost,它的繼承關係圖如下所示:


Context

在Tomcat中,每一個執行的webapp其實最終都是以Context的形成存在,每個Context都有一個根路徑和請求URL路徑,Context對應原始碼中的org.apache.catalina.core.StandardContext,它的繼承關係圖如下圖所示:



在Tomcat中我們通常採用如下的兩種方式建立一個Context.下面分別描述一下:

  1. <CATALINA-HOME>\webapps目錄中建立一個目錄,這個時候將自動建立一個context,預設context的訪問url為http://host:port/dirname,你也可以通過在ContextRoot\META-INF中建立一個context.xml的檔案,其中包含如下的內容來指定應用的訪問路徑。 <Context path="/yourUrlPath" />
  2. conf\server.xml檔案中增加context元素。 第二種建立context的方法,我們可以選擇在server.xml檔案的<Host>元素,比如我們在server.xml檔案中增加如下內容:
 .......
<span style="line-height: 1.5;">  </span><span style="line-height: 1.5;">.........</span>
      <Context path="/mypath" docBase="/Users/xxx" reloadable="true">
        </Context>
  </Host>
</Engine>
  </Service>
</Server>
這樣的話,我們就可以通過http://host:port/mypath訪問上面配置的context了。

Valve

Valve中文意思是閥門,Valve是Tomcat中責任鏈模式的實現,通過連結多個Valve對請求進行處理。每個容器都有一個流水線Pipeline(過濾器鏈),每個流水線至少有一個閥門。其中Valve可以定義在任何的Container中,上面說的Engine,Host,Context都屬於容器。tomcat 預設定義了一個名為org.apache.catalina.valves.AccessLogValve的Valve,這個Valve負責攔截每個請求,然後記錄一條訪問日誌。

通過上面的分析,我們發現Server,Service,Engine,Host,Context都實現了org.apache.catalina.Lifecycle介面,通過這個介面管理了這些核心元件的生命週期,關於這些元件的生命週期,我們在下一篇文章描述。