1. 程式人生 > >Tomcat8原始碼分析系列-tomcat框架設計

Tomcat8原始碼分析系列-tomcat框架設計

總體架構

如上圖所示,tomcat由Server、Service、Engine、Connerctor、Host、Context元件組成,其中帶有s的代表在一個tomcat例項上可以存在多個元件,比如Context(s),tomcat允許我們部署多個應用,每個應用對應一個Context。這些元件在tomcat的conf/server.xml檔案中可以找到,對tomcat的調優需要改動該檔案

server.xml
<Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout
="20000" redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8009" 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" unpackWARs="true" autoDeploy="true"> <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

image

Server元件對應org.apache.catalina.Server介面,類圖如上所示。
- Server繼承至LifeCycle,LifeCycle是一個非常重要的介面,各大元件都繼承了這個介面,用於管理tomcat的生命週期,比如init、start、stop、destory;另外,它使用了觀察者模式,LifeCycle是一個監聽者,它會向註冊的LifecycleListener觀察者發出各種事件
- Server提供了findService、getCatalina、getCatalinaHome、getCatalinaBase等介面,支援查詢、遍歷Service元件,這裡似乎看到了和Serivce元件的些許聯絡

public interface Server extends Lifecycle {

    public NamingResourcesImpl getGlobalNamingResources();

    public void setGlobalNamingResources(NamingResourcesImpl globalNamingResources);

    public javax.naming.Context getGlobalNamingContext();

    public int getPort();

    public void setPort(int port);

    public String getAddress();

    public void setAddress(String address);

    public String getShutdown();

    public void setShutdown(String shutdown);

    public ClassLoader getParentClassLoader();

    public void setParentClassLoader(ClassLoader parent);

    public Catalina getCatalina();

    public void setCatalina(Catalina catalina);

    public File getCatalinaBase();

    public void setCatalinaBase(File catalinaBase);

    public File getCatalinaHome();

    public void setCatalinaHome(File catalinaHome);

    public void await();

    public Service findService(String name);

    public Service[] findServices();

    public void removeService(Service service);

    public Object getNamingToken();
}

Service

Service的預設實現類是StardardService,類結構和StardardServer很相似,也是繼承至LifecycleMBeanBase,實現了Service介面

由Service介面不難發現Service元件的內部結構
- 持有Engine例項
- 持有Server例項
- 可以管理多個Connector例項
- 持有Executor引用

public class StandardService extends LifecycleMBeanBase implements Service {
    // 省略若干程式碼
}

public interface Service extends Lifecycle {

    public Engine getContainer();

    public void setContainer(Engine engine);

    public String getName();

    public void setName(String name);

    public Server getServer();

    public void setServer(Server server);

    public ClassLoader getParentClassLoader();

    public void setParentClassLoader(ClassLoader parent);

    public String getDomain();

    public void addConnector(Connector connector);

    public Connector[] findConnectors();

    public void removeConnector(Connector connector);

    public void addExecutor(Executor ex);

    public Executor[] findExecutors();

    public Executor getExecutor(String name);

    public void removeExecutor(Executor ex);

    Mapper getMapper();
}

Connector

Connector是tomcat中監聽TCP埠的元件,server.xml預設定義了兩個Connector,分別用於監聽http、ajp埠。對應的程式碼是org.apache.catalina.connector.Connector,它是一個實現類,並且實現了Lifecycle介面

http

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

http對應的Connector配置如上所示,其中protocol用於指定http協議的版本,還可以支援2.0;connectionTimeout定義了連線超時時間,單位是毫秒;redirectPort是SSL的重定向埠,它會把請求重定向到8443這個埠

AJP

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Apache jserver協議(AJP)是一種二進位制協議,它可以將來自web伺服器的入站請求傳送到位於web伺服器後的應用伺服器。如果我們希望把Tomcat整合到現有的(或新的)Apache http server中,並且希望Apache能夠處理web應用程式中包含的靜態內容,或者使用Apache的SSL處理,我們便可以使用該協議。但是,在實際的專案應用中,AJP協議並不常用,大多數應用場景會使用nginx+tomcat實現負載。

Container

org.apache.catalina.Container介面定義了容器的api,它是一個處理使用者servlet請求並返回物件給web使用者的模組,它有四種不同的容器:
- Engine,表示整個Catalina的servlet引擎
- Host,表示一個擁有若干個Context的虛擬主機
- Context,表示一個Web應用,一個context包含一個或多個wrapper
- Wrapper,表示一個獨立的servlet

Container介面

Engine、Host、Context、Wrapper都有一個預設的實現類StandardXXX,均繼承至ContainerBase。此外,一個容器還包含一系列的Lodder、Logger、Manager、Realm和Resources等

一個容器可以有一個或多個低層次上的子容器,並且一個Catalina功能部署並不一定需要全部四種容器。一個Context有一個或多個wrapper,而wrapper作為容器層次中的最底層,不能包含子容器。從一個容器新增到另一容器中可以使用在Container介面中定義的addChild()方法義:

public void addChild(Container child); 

刪除一個容器可以使用Container介面中定義的removeChild()方法:

public void removeChild(Container child); 

另外容器介面支援子介面查詢和獲得所有子介面集合的方法findChild和findChildren方法:

public Container findChild(String name); 
public Container[] findChildren(); 

Engine

Engine類圖

Engine表示Catalina的Servlet引擎,如果使用了Engine的話,則它是Catalina的頂層容器,因此在StardardCataline的setParent()方法中直接丟擲的異常

public interface Engine extends Container {

    public String getDefaultHost();

    public void setDefaultHost(String defaultHost);

    public String getJvmRoute();

    public void setJvmRoute(String jvmRouteId);

    public Service getService();

    public void setService(Service service);
}

public class StandardEngine extends ContainerBase implements Engine {

    // other code...

    public void setParent(Container container) {
        throw new IllegalArgumentException(sm.getString("standardEngine.notParent"));
    }
}

server.xml

<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" unpackWARs="true" autoDeploy="true">
    <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>

Host

Host定義了一個虛擬主機,正所謂虛擬主機,當然是可以用來部署應用程式的,Tomcat的Host也是如此。它在server.xml中定義了一個localhost的Host,應用根目錄在webapps下面,預設是支援解壓重新部署的。

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

Context

Context類圖

Context代表一個獨立的web應用,針對每個Context,tomcat都是使用不同的Classloader避免類衝突。如果我們希望使用一個自定義的目錄作為部署路徑的話,可以在server.xml中新增Context即可

<Context path="/static" docBase="D:/static" reloadable="true"></Context>

程式碼模組簡介

catalina包

Tomcat的核心模組,包括了HttpServlet、HttpSession的實現,以及各大元件的實現,這塊的程式碼量是最多的,也是最複雜的一部分

coyote包

這塊主要用於支援各種協議,比如http1.1、http2.0、ajp等,程式碼量較少

tomcat包

tomcat的基礎包,包括了資料庫連線池、websocket實現、tomcate的jni、工具類。org.apache.tomcat.util包的程式碼量也不少,其中還包括了對jdk原始碼的擴充套件,比如執行緒池。

下圖羅列各個模組的大致用途以及程式碼量
image

參考資料