1. 程式人生 > >Tomcat原始碼簡單分析

Tomcat原始碼簡單分析

前言

Tomcat是目前應用比較多的servlet容器,有複雜的結構,想要了解它,就應該順著開發者設計之初的思路來,先了解整體的結構,對整體有了一定的掌控後,再逐個分析,瞭解感興趣的細節。

架構設計

 

簡單介紹各個模組:

  • Server:伺服器的意思,代表整個tomcat伺服器,一個tomcat只有一個Server;

  • Service:Server中的一個邏輯功能層, 一個Server可以包含多個Service;

  • Connector:稱作聯結器,是Service的核心元件之一,一個Service可以有多個Connector,主要是連線客戶端請求;

  • Container:Service的另一個核心元件,按照層級有Engine,Host,Context,Wrapper四種,一個Service只有一個Engine,其主要作用是執行業務邏輯;

server.xml配置檔案 

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

    <GlobalNamingResources>
        <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>

    <Service name="Catalina">
        <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>

Tomcat啟動流程

啟動Tomcat會是執行startup.bat或者startup.sh檔案,這兩個檔案最後都會呼叫,org.apache.catalina.startup包下面Bootstrap類的main方法。

main方法先例項化了一個Bootstrap例項,接著呼叫了init方法。init方法是生命週期方法,接著看init的具體實現。

init方法,先初始化了類載入器。initClassLoaders方法具體實現如下:

è¿éåå¾çæè¿°

Tomcat執行的是start操作,呼叫完init方法後,會執行load方法。

load方法通過反射呼叫Catalina類的load方法。

è¿éåå¾çæè¿°

load方法中比較重要的方法是createStartDigester(),createStartDigester方法主要的作用就是幫我們例項化了所有的服務元件包括server,service和connect。

初始化操作完成後,接下來會執行catalina例項的start方法。Tomcat會預設載入org.apache.catalina.core.StandardServer作為Server的例項類。

在Server的start的方法裡面會執行service的start方法。在createStartDigester()方法裡面,會預設載入org.apache.catalina.core.StandardService類。會接著呼叫Service的start方法。

è¿éåå¾çæè¿°

service中會呼叫connector的start方法。至此Tomcat啟動完畢

Tomcat執行流程

假設來自客戶的請求為:http://localhost:8080/test/index.jsp.請求被髮送到本機埠8080,被在那裡偵聽的Coyote HTTP/1.1 Connector獲得,然後

  • Connector把該請求交給它所在的Service的Engine來處理,並等待Engine的迴應

  • Engine獲得請求localhost:8080/test/index.jsp,匹配它所有虛擬主機Host

  • Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的預設主機)

  • localhost Host獲得請求/test/index.jsp,匹配它所擁有的所有Context

  • Host匹配到路徑為/test的Context(如果匹配不到就把該請求交給路徑名為""的Context去處理)

  • path="/test"的Context獲得請求/index.jsp,在它的mapping table中尋找對應的servlet

  • Context匹配到URL PATTERN為*.jsp的servlet,對應於JspServlet類,構造HttpServletRequest物件和HttpServletResponse物件,作為引數呼叫JspServlet的doGet或doPost方法

  • Context把執行完了之後的HttpServletResponse物件返回給Host

  • Host把HttpServletResponse物件返回給Engine

  • Engine把HttpServletResponse物件返回給Connector

  • Connector把HttpServletResponse物件返回給客戶browser

 Server

Server是Tomcat最頂層的容器,代表著整個伺服器,即一個Tomcat只有一個Server,Server中包含至少一個Service元件,用於提供具體服務。這個在配置檔案中也得到很好的體現(port=”8005” shutdown=”SHUTDOWN”是在8005埠監聽到”SHUTDOWN”命令,伺服器就會停止)。

Tomcat中其標準實現是:org.apache.catalina.core.StandardServer類,繼承LifecycleMBeanBase,,tomcat為所有的元件都提供了生命週期管理。

Service

在conf/server.xml檔案中,可以看到Service元件包含了Connector元件和Engine元件(前面有提過,Engine就是一種容器),即Service相當於Connector和Engine元件的包裝器,將一個或者多個Connector和一個Engine建立關聯關係。在預設的配置檔案中,定義了一個叫Catalina 的服務,它將HTTP/1.1和AJP/1.3這兩個Connector與一個名為Catalina 的Engine關聯起來。

一個Server可以包含多個Service(它們相互獨立,只是公用一個JVM及類庫),一個Service負責維護多個Connector和一個Container。其標準實現是StandardService。

Connector

Connector主要負責處理與客戶端的通訊,Connector的例項用於監聽埠,接受來自客戶端的請求並將請求轉交給Engine處理,同時將來自Engine的答覆返回給客戶端。

connector執行流程

Tomcat原始碼中與connector相關的類位於org.apache.coyote包中,Connector分為以下幾類:

Http Connector, 基於HTTP協議,負責建立HTTP連線。它又分為BIO Http Connector與NIO Http Connector兩種,後者提供非阻塞IO與長連線Comet支援。預設情況下,Tomcat使用的就是這個Connector。

AJP Connector, 基於AJP協議,AJP是專門設計用來為tomcat與http伺服器之間通訊專門定製的協議,能提供較高的通訊速度和效率。如與Apache伺服器整合時,採用這個協議。

APR HTTP Connector, 用C實現,通過JNI呼叫的。主要提升對靜態資源(如HTML、圖片、CSS、JS等)的訪問效能。現在這個庫已獨立出來可用在任何專案中。Tomcat在配置APR之後效能非常強勁。

Connector使用ProtocolHandler來處理請求的,不同的ProtocolHandler代表不同的連線型別,比如:Http11Protocol使用的是普通Socket來連線的(tomcat9已經刪除了這個類,不再採用BIO的方式),Http11NioProtocol使用的是NioSocket來連線的。

其中ProtocolHandler由包含了三個部件:Endpoint、Processor、Adapter。 
1. Endpoint用來處理底層Socket的網路連線,Processor用於將Endpoint接收到的Socket封裝成Request(這個Request和ServletRequest無關),Adapter充當介面卡,用於將Request轉換為ServletRequest交給Container進行具體的處理。 
2. Endpoint由於是處理底層的Socket網路連線,因此Endpoint是用來實現TCP/IP協議的,而Processor用來實現HTTP協議的,Adapter將請求適配到Servlet容器進行具體的處理。 
3. Endpoint的抽象實現AbstractEndpoint裡面定義的Acceptor和AsyncTimeout兩個內部類和一個Handler介面。Acceptor用於監聽請求,AsyncTimeout用於檢查非同步Request的超時,Handler用於處理接收到的Socket,在內部呼叫Processor進行處理。

下面我們以org.apache.coyote.http11.Http11AprProtocol為例說明Connector的工作流程。

①它將工作委託給AprEndpoint類。

public Http11AprProtocol() {  
        endpoint = new AprEndpoint();     // 主要工作由AprEndpoint來完成  
        cHandler = new Http11ConnectionHandler(this);    // inner class  
        ((AprEndpoint) endpoint).setHandler(cHandler);

②在AprEndpoint.Acceptor類中的run()方法會接收一個客戶端新的連線請求.

protected class Acceptor extends AbstractEndpoint.Acceptor {  
  
    private final Log log = LogFactory.getLog(AprEndpoint.Acceptor.class);  
  
    @Override  
    public void run() {  
  
        int errorDelay = 0;  
  
        // Loop until we receive a shutdown command  
        while (running) {  

③在AprEndpoint類中,有一個內部介面Handler,該介面定義如下:

public interface Handler extends AbstractEndpoint.Handler {  
        public SocketState process(SocketWrapper<Long> socket,  
                SocketStatus status);  
    }

 

④在Http11AprProtocol類中實現了AprEndpoint中的Handler介面,

protected static class Http11ConnectionHandler  
           extends AbstractConnectionHandler<Long,Http11AprProcessor> implements Handler {  


並呼叫Http11AprProcessor類(該類實現了ActionHook回撥介面)。

protected Http11AprProcessor createProcessor() {  
            Http11AprProcessor processor = new Http11AprProcessor(

Container 

Tomcat提供了一個Container介面來抽象容器,並且細分了4種類型的容器,分別是Engine、Host、Context和Wrapper,對應不同的概念層次。

· Engine:表示整個Catalina的servlet引擎

· Host:表示一個擁有數個上下文的虛擬主機

· Context:表示一個Web應用,一個context包含一個或多個wrapper

· Wrapper:表示一個獨立的servlet