深入理解Tomcat系列之一:系統架構(轉)


前言

Tomcat是Apache基金組織下的開源項目,性質是一個Web服務器。下面這種情況很普遍:在eclipse床架一個web項目並部署到Tomcat中,啟動tomcat,在瀏覽器中輸入一個類似http://localhost:8080/webproject/anyname.jsp的url,然后就可以看到我們寫好的jsp頁面的內容了。一切都是那么自然和順理成章,然而這一切都是源於tomcat帶給我們的,那么在tomcat背后,這一切又是怎么樣發生的呢?帶着對tomcat工作原理的好奇心,我決定研究一下tomcat的源碼,然而部署源碼環境的過程卻讓我心灰意冷,本着搞不定我還真不信的熱情,折騰了一個晚上+一個早上,終於把源碼源碼環境搭建好了。 

為了讓文章顯得更有條理性,我將從以下幾個方面說明Tomcat的工作流程:

  • 搭建Tomcat源碼環境指導
  • Tomcat的系統架構
  • Tomcat中的核心組件說明
  • Servlet工作原理
  • 一個例子

Tomcat的系統架構

首先我們從一個宏觀的角度來看一下Tomcat的系統的架構:

Tomcat

從這張圖中可以看到,Tomcat的核心組件就兩個Connector和Container(后面還有詳細說明),一個Connector+一個Container構成一個Service,Service就是對外提供服務的組件,有了Service組件Tomcat就可以對外提供服務了,但是光有服務還不行,還得有環境讓你提供服務才行,所以最外層的Server就為Service提供了生存的土壤。那么這些個組件到底是干嘛用的呢?Connector是一個連接器,主要負責接收請求並把請求交給Container,Container就是一個容器,主要裝的是具體處理請求的組件。Service主要是為了關聯Container與Connector,一個單獨的Container或者一個單獨的Connector都不能完整處理一個請求,只有兩個結合在一起才能完成一個請求的處理。Server這是負責管理Service集合,從圖中我們看到一個Tomcat可以提供多種服務,那么這些Serice就是由Server來管理的,具體的工作包括:對外提供一個接口訪問Service,對內維護Service集合,維護Service集合又包括管理Service的生命周期、尋找一個請求的Service、結束一個Service等。以上就是對Tomcat的核心組件的簡要說明,下面我們詳細看看每一個組件的執行流程:

Server

上面說Server是管理Service接口的,Server是Tomcat的頂級容器,是一個接口,Server接口的標准實現類是StandardServer類,在Server接口中有許多方法,我們重點關注兩個方法:addService()和findService(String)。我們先來看看Server接口的全貌:

Server接口全貌

接着看看addService()和findService(String)的實現代碼:

代碼清單1-1:

/**
 * Add a new Service to the set of defined Services.
 *
 * @param service The Service to be added
 */
@Override
public void addService(Service service) {

    service.setServer(this);

    synchronized (services) {
        Service results[] = new Service[services.length + 1];
        System.arraycopy(services, 0, results, 0, services.length);
        results[services.length] = service;
        services = results;

        if (getState().isAvailable()) {
            try {
                service.start();
            } catch (LifecycleException e) {
                // Ignore
            }
        }

        // Report this property change to interested listeners
        support.firePropertyChange("service", null, service);
    }

}

可以看到,Server使用一個數組來管理Service的,每添加一個Service就把原來的Service拷貝到一個新的數組中,再把新的Service放入Service數組中。所以Server與Service是關聯在一起的,那么后面的getState().isAvailable()是干嘛的呢?判斷狀態是否無效,從而決定是否執行service方法。這里說到了狀態,就不得不說Tomcat管理各組件生命周期的Lifecycle接口了:

Lifecycle接口

Tomcat中的組件都交給這個接口管理,但是具體組件的生命周期是由包含組件的父容器來管理的,Tomcat中頂級容器管理着Service的生命周期,Service容器又是Connector和Container的父容器,所以這兩個組件的生命周期是由Service管理的,Container也有子容器,所以管理着這些子容器的生命周期。這樣,只要所有組件都實現了Lifecycle接口,從頂層容器Server開始,就可以控制所有容器的生命周期了。Lifecycle接口中定義了很多狀態,在api中詳細說明了調用不同方法后的狀態轉變,同時定義了不同的方法,這些方法在執行后狀態會發生相應的改變,在Lifecycle接口中定義了如下方法:

Lifecycle接口方法

在StandServer中實現了startInernal()方法,就是循環啟動StandServer管理的Service的過程,Tomcat的Service都實現了Lifecycle接口,所以被管理的Service都將被通知到,從而執行start()方法,startIntenal()方法是這樣的:

代碼清單1-2:

/**
 * Start nested components ({@link Service}s) and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected void startInternal() throws LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    // Start our defined Services
    synchronized (services) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

現在所有的Service就會收到通知繼而執行start方法。如果一個Service不允許被使用將會拋出一個LifecycleException異常。

stopIntenal()會通知所有Service執行stop方法,具體處理流程與startIntenal()方法類似。這個執行過程涉及一個非常重要的設計模式,就是觀察者模式

現在我們已經能夠知道了容器通過Lifecycle接口管理容器的生命周期,那么在父容器的狀態改變具體是怎么樣通知給子容器的呢?回到代碼清單1-2,我們注意到有一個fireLifecycleEvent()方法,fireLifecycleEvent()的執行流程如下:

  1. 調用LifecycleBase的fireLifecycleEvent(LifecycleListener listener)方法,LifecycleBase是一個抽象類,實現了Lifecycle接口
  2. 繼續調用LifecycleSupport(是一個輔助完成對已經注冊監聽器的事件通知類,不可被繼承,使用final)的fireLifecycleEvent(String type, Object data)方法
  3. 完成事件通知

fireLifecycleEvent(String type, Object data)的方法如下:

代碼清單1-3:

/**
 * Notify all lifecycle event listeners that a particular event has
 * occurred for this Container.  The default implementation performs
 * this notification synchronously using the calling thread.
 *
 * @param type Event type
 * @param data Event data
 */
public void fireLifecycleEvent(String type, Object data) {

    LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
    LifecycleListener interested[] = listeners;
    for (int i = 0; i < interested.length; i++)
        interested[i].lifecycleEvent(event);

}

所以,具體事件的通知是由LifecycleListener接口的lifecycleEvent方法完成的,各實現類可以根據不同的情況實現不同的事件監聽邏輯

Service

Service是具體提供服務的接口,一個Service包裝了Connector和一個Container,在Tomcat中這點是如何實現的呢?Service是一個接口,其標准實現類是StandardService,下面是這兩個類的鳥瞰圖:

Service接口 
StandardService類

這里,我們只關心與Connector和Container最緊密的方法:setContainer()和addConnector()方法,先看一下setContainer()方法的源碼:

代碼清單2-1:

/**
 * Set the <code>Container</code> that handles requests for all
 * <code>Connectors</code> associated with this Service.
 *
 * @param container The new Container
 */
@Override
public void setContainer(Container container) {

    Container oldContainer = this.container;
    if ((oldContainer != null) && (oldContainer instanceof Engine))
        ((Engine) oldContainer).setService(null);
    this.container = container;
    if ((this.container != null) && (this.container instanceof Engine))
        ((Engine) this.container).setService(this);
    if (getState().isAvailable() && (this.container != null)) {
        try {
            this.container.start();
        } catch (LifecycleException e) {
            // Ignore
        }
    }
    if (getState().isAvailable() && (oldContainer != null)) {
        try {
            oldContainer.stop();
        } catch (LifecycleException e) {
            // Ignore
        }
    }

    // Report this property change to interested listeners
    support.firePropertyChange("container", oldContainer, this.container);

}

從代碼中可以看到這個方法主要的任務是設置一個Container容器來處理一個或者多個Connector傳送過來的請求。首先判斷當前的Service是否已經關聯了Container容器,如果已經關聯了就去除這個關聯關系。如果原來的Container容器已經啟動了就終止其生命周期,結束運行並設置新的關聯關系,這個新的Container容器開始新的生命周期。最后把這個過程通知給感興趣的事件監聽程序。

下面看看addConnector的方法:

代碼清單2-2:

/**
 * Add a new Connector to the set of defined Connectors, and associate it
 * with this Service's Container.
 *
 * @param connector The Connector to be added
 */
@Override
public void addConnector(Connector connector) {

    synchronized (connectors) {
        connector.setService(this);
        Connector results[] = new Connector[connectors.length + 1];
        System.arraycopy(connectors, 0, results, 0, connectors.length);
        results[connectors.length] = connector;
        connectors = results;

        if (getState().isAvailable()) {
            try {
                connector.start();
            } catch (LifecycleException e) {
                log.error(sm.getString(
                        "standardService.connector.startFailed",
                        connector), e);
            }
        }

        // Report this property change to interested listeners
        support.firePropertyChange("connector", null, connector);
    }

}

執行過程也比較清楚:用一個同步代碼塊包住connectors數組,首先設置connector與container和service的關聯關系,然后讓connector開始新的生命周期,最后通知感興趣的事件監聽程序。注意到Connector的管理和Server管理Service一樣都使用了數組拷貝並把新的數組賦給當前的數組,從而間接實現了動態數組。之所以使用數組我想可能是出於性能的考慮吧。

http://blog.csdn.net/u011116672/article/details/50993980

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM