1. 程式人生 > >深入理解Tomcat系列之一:系統架構

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

chan 比較 efi 多個 soci 重點 之一 處理流程 containe

前言:

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的核心組件就兩個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接口的全貌:

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

代碼清單1-1:

/**

  • Add a new Service to the set of defined Services.
  • @param service The Service to be added*/

    @Override
    br/>*/
    @Override

    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接口中定義了如下方法:

在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
    br/>*/
    @Override

    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()的執行流程如下:

調用LifecycleBase的fireLifecycleEvent(LifecycleListener listener)方法,LifecycleBase是一個抽象類,實現了Lifecycle接口
繼續調用LifecycleSupport(是一個輔助完成對已經註冊監聽器的事件通知類,不可被繼承,使用final)的fireLifecycleEvent(String type, Object data)方法
完成事件通知
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,下面是這兩個類的鳥瞰圖:

這裏,我們只關心與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
    br/>*/
    @Override

    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
    br/>*/
    @Override

    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一樣都使用了數組拷貝並把新的數組賦給當前的數組,從而間接實現了動態數組。之所以使用數組我想可能是出於性能的考慮吧。

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