tomcat原理詳解

tomcat的啓動是經過Bootstrap類的main方法(tomcat6開始也能夠直接經過Catlina的main啓動)java

 

Bootstrap的啓動

Bootstrap的main方法先new了一個本身的對象(Bootstrap),而後用該對象主要執行了四個方法:web

init();apache

setAwait(true);數組

load(args);瀏覽器

start();tomcat

 

init():初始化了ClassLoader(類加載器,沒它的話後面就無法加載其餘類了),而後用ClassLoader建立了Catlina實例。安全

 

setAwait(true),load(args),start();這三個方法實際上都是經過反射調用的Catlina實例中所對應的這三個方法。服務器

 

Catlina的啓動

首先Catlina中的setAwait(true)方法:網絡

這個方法設置的值(true或false),是留給後面用的。當經過後面的start()方法啓動完服務器後,會檢查這個值爲true仍是false,若是爲true,調用Catlina的await()方法,tomcat繼續開着。若是爲false,則調用Catlina的stop()方法,關閉tomcat。session

 

而後是Catlina中的load(args)方法:

Catalina將利用Digest解析server.xml(server的配置文件,server是最頂層容器),並根據server的配置文件建立server實例。

而後調用server實例的init()方法,進行server的初始化。

Server初始化時,又會調用其內部的Service的init方法(server只有一個,但service有多個)。

大致流程以下圖:

 

而後是Catlina中的start()方法:

Catlina的start()方法主要調用了server的start()方法來啓動服務器,而後server的start()方法再調用多個service的start()方法。

大致流程以下圖:

 

Server的啓動

由上面Catlina的啓動咱們知道了,

Catlina中的load(args)方法使得Server進行了初始化,

Catlina中的start()方法使得Server也調用了start方法。

 

Server是一個接口,它的默認實現類是StandardServer,因此上面和以後咱們操做的Server其實是操做的StandardServer。

 

StandardServer繼承自LifecycleMBeanBase,而LifecycleMBeanBase又繼承自LifecycelBean。

根據以前的分析咱們知道,StandardServer主要要調用兩個方法:init()和start()。

而這兩個方法都不在StandardServer中,實際上都在StandardServer的父類LifecycelBean中。而LifecycelBean中的這兩個方法,實際上由「回頭」調用了StandardServer中的initInternal()和startInternal()方法。

 

也就是StandardServer調用了init()其實是調用了本身的initInternal()方法。

StandardServer調用了start()其實是調用了本身的startInternal()方法。

 

initInternal()方法和startInternal()方法「分別循環調用了每個service的init()方法和start()方法」。

 

除以上方法外,Standard還有addService()方法用來添加service,和remove()方法用來刪除service。

 

另外Standard還擁有await()方法,用法在上面Catlina的啓動中講了。

 

Service的啓動

一個Serve中能夠有多個service,Service也是接口,Service接口的默認實現類是StandardService,因此上面和後面討論的service其實是在操做StandardService。

 

StandardService也繼承於LifecycleMBeanBase,而LifecycleMBeanBase繼承於LifecycelBean,LifecycelBean中存在init()和start()方法。

因此Server循環調用每一個StandardService的init()和start()方法,實際上是調用的StandardService的父類LifecycelBean中的init()和start()方法,而後init()和start()方法在「回頭調用」StandardService中的initInternal()方法和startInternal()方法

 

也就是說Server循環調用每一個StandardService的init()和start()方法,最後執行的是每一個Service的initInternal()方法和startInternal()方法。

 

每個service都是一個對外提供服務的組件,每個Service中都包含:一個Container+多個Connector,因此service中的方法,都是用來操做這兩種類的。

 

initInternal()方法:

下面是initInternal源碼:

protected void initInternal() throws LifecycleException {
    super.initInternal();
    if(this.container != null) {
        this.container.init();
    }

    Executor[] arr$ = this.findExecutors();
    int arr$1 = arr$.length;

    int len$;
    for(len$ = 0; len$ < arr$1; ++len$) {
        Executor i$ = arr$[len$];
        if(i$ instanceof JmxEnabled) {
            ((JmxEnabled)i$).setDomain(this.getDomain());
        }

        i$.init();
    }

    this.mapperListener.init();
    Object var11 = this.connectorsLock;
    synchronized(this.connectorsLock) {
        Connector[] var12 = this.connectors;
        len$ = var12.length;

        for(int var13 = 0; var13 < len$; ++var13) {
            Connector connector = var12[var13];

            try {
                connector.init();
            } catch (Exception var9) {
                String message = sm.getString("standardService.connector.initFailed", new Object[]{connector});
                log.error(message, var9);
                if(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new LifecycleException(message);
                }
            }
        }

    }
}

首先,調用Container的init()方法,

再調用mapperListener的init()方法(mapperListener是用來監聽container的變化的),

再調用「多個」excutor的init()方法(excutor是用來在connector中管理線程池的),

再調用Connctor的init()方法。

 

startInternal()方法:

與上同理,也是分別調用四種類的start()方法。

 

總結一下,Server和Service的關係大體以下圖(須要說明的是,下圖中每一個類或接口中所包含的方法我並無全寫出來,只寫了與流程相關的幾個):

 

Lifecycle接口

將到這裏應該提一下Lifecycle接口,該接口的默認實現就是LifecycleBean類。

經過上面提到Server和Service子類咱們發現他們都繼承了LifecycleBean類,並調用了該類的init()和start()方法。

實際上凡是擁有「生命週期」的組件都實現了Lifecycle接口,Lifecycle接口的做用就是進行「生命週期的規定」。

該接口主要作了如下四件事:

 

1、定義了13個String常量,這些常量被用於定義「LifecycleEvent事件」的type屬性中 。這樣設計的好處是,只須要發送一種類型事件LifecycleEvent,而根據裏面type屬性的值的不一樣,來斷定是什麼事件。(不然13種事件就得定義13中事件類)。

 

2、定義了三個管理監聽的方法,分別用來添加、查找、刪除LifecycleListener類型的監聽器。

 

3、定義了四個生命週期方法:init()、start()、stop()、destroy(),用於執行生命週期各個階段的操做。

 

4、定義了用來獲取當前狀態的兩個方法。

 

LifecycleBase實現類

lifecycleBase是Lifecycle接口的默認實現類,全部擁有生命週期的組件都直接或間接的繼承自lifecycleBase類,lifecycleBase爲lifecycle接口中的各個方法提供了默認實現。

 

三個管理監聽的方法:

這三個方法分別是addLifecycleListener(添加監聽器)、findLifecycleListeners(查找監聽器)、removeLifecycleListener(刪除監聽器)。

監聽器的管理專門使用了一個「LifecycleSupport類」來完成。

這三個方法都調用了LifecycleSupport中的同名方法。

LifecycleSupport中經過一個數組來保存現有個監聽器。

另外LifecycleSupport中還定義了處理LifecycleEvent時間的方法。

 

四個生命週期方法:

主要就是init()和start()方法,在調用方法以前會先判斷當前狀態與要調用的方法是否匹配,若是不匹配則會執行相應的方法使其匹配(如在低啊用init以前先調用了start,這是就會先執行init)。

以後會調用相應的模板方法(initInternal()和startInternal())讓子類具體執行init和start。

咱們先看一下init()方法的源碼:

public final synchronized void init() throws LifecycleException {
    if(!this.state.equals(LifecycleState.NEW)) {
        this.invalidTransition("before_init");
    }

    this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);

    try {
        this.initInternal();
    } catch (Throwable var2) {
        ExceptionUtils.handleThrowable(var2);
        this.setStateInternal(LifecycleState.FAILED, (Object)null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.initFail", new Object[]{this.toString()}), var2);
    }

    this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);
}

能夠看出,init()方法先將當前狀態設置成了INITIALIZING,而後執行的是具體實現類的initInternal()方法。

 

再看看start()的源碼:

public final synchronized void start() throws LifecycleException {
    if(!LifecycleState.STARTING_PREP.equals(this.state) && !LifecycleState.STARTING.equals(this.state) && !LifecycleState.STARTED.equals(this.state)) {
        if(this.state.equals(LifecycleState.NEW)) {
            this.init();
        } else if(this.state.equals(LifecycleState.FAILED)) {
            this.stop();
        } else if(!this.state.equals(LifecycleState.INITIALIZED) && !this.state.equals(LifecycleState.STOPPED)) {
            this.invalidTransition("before_start");
        }

        this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null, false);

        try {
            this.startInternal();
        } catch (Throwable var2) {
            ExceptionUtils.handleThrowable(var2);
            this.setStateInternal(LifecycleState.FAILED, (Object)null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", new Object[]{this.toString()}), var2);
        }

        if(this.state.equals(LifecycleState.FAILED)) {
            this.stop();
        } else if(!this.state.equals(LifecycleState.STARTING)) {
            this.invalidTransition("after_start");
        } else {
            this.setStateInternal(LifecycleState.STARTED, (Object)null, false);
        }

    } else {
        if(log.isDebugEnabled()) {
            LifecycleException t = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}), t);
        } else if(log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}));
        }

    }
}

由此可看到start方法判斷了當前狀態,若是狀態正確執行的起始是子類的startInternal()方法

 

兩個獲取當前狀態的方法:

在生命週期的相應方法中已經設置了state屬性,這兩個方法就是返回該屬性。

 

Connector

Connector的主要任務是負責處理瀏覽器發送過來的請求,並建立一個Request和Response的對象用於和瀏覽器交換數據,而後產生一個線程用於處理請求,Connector會把Request和Response對象傳遞給該線程,該線程的具體的處理過程是Container容器的事了。Container就是Servlet的容器,Container處理完後會吧結果返回給Connector,最後Connector使用socket將處理結果返回給客戶端,整個請求處理完畢。

Connector的底層是使用socket進行鏈接,Request和Response時按照http協議(協議可選)來封裝,因此Connector同時實現了TCP/IP和HTTP協議。

很明顯Connector應該能夠有多個,由於一次請求對應一個Connector,統一時間可能有多個請求。

 

Connector能夠處理不一樣協議類型的請求,默認處理的是HTTP1.1協議。

Connector中使用ProtocolHandler來具體處理請求,不一樣的ProtocolHandler表明不一樣協議的請求類型。

ProtocolHandler中又包含三個重要的組件:

Endpoint,用於處理底層socket網絡鏈接。

Processor,用於將socket接收到的socket封裝成request。

Adapter,用於將requst交給Container進行處理。

 

具體執行順序以下

1、以前講過了,Catlina會調用load方法,根據server.xml配置文件建立Server對象,以後Server由調用init()建立service,service再調用Connector的init方法建立並初始化。

因此Connector是在Catlina會調用load方法時建立的。

 

2、Conntctor首先執行的是初始化init,Conntctor的初始化主要是用來初始化ProtocolHandler。

因此Conntctor執行init後:

一、先建立一個Adapter,並設置到ProtocolHandler中(這個Adapter後面要用,也就是上面所提到的用於將requst交給Container進行處理。)

二、調用ProtocolHandler的init方法,讓它進行初始化。

三、調用mapperListener的init方法初始化(mapperListener做用是用來監聽容器,容器發生變化會被通知)

 

3、剛剛上面提到了ProtocolHandler的init方法被調用了,咱們在看看ProtocolHandler初始化都作了什麼。

整個ProtocolHandler結構以下圖:

ProtocolHandler的初始化方法被實現於「AbstractProtocol抽象類」中。

該抽象類有兩種子抽象類:AbstractAjpProtocol和AbstractHttp11Protocol,分別對應了兩種不一樣的請求協議。

因此最終咱們初始化的步驟實際上是在AbstractHttp11Protocol抽象類中執行(當協議爲HTTP時)。

配置文件server.xml中能夠指明須要建立的ProtocolHandler類型,默認是處理HTTP1.1協議的ProtocolHandler,以後咱們真正建立的就是AbstractHttp11Protocol抽象類的某一個子類(如Http11NioProtocol)

AbstractHttp11Protocol抽象類的初始化主要是調用了Endpoint的init初始化。

 

4、endpoint的init方法在抽象父類AbstractEndpoint中,是個模板方法,實際上調用的是子類NioEndpoint裏面的bind()方法。

這裏的bind()方法主要做用是,檢查Acceptor和poller的線程數量是否正確(必須大於等於1)。

 

提一下Acceptor和poller這兩個類,Acceptor的做用是控制與tomcat創建鏈接的數量,但Acceptor只負責創建鏈接。socket內容的讀寫是經過Poller來實現的。

 

此時Connector的init初始化就完成了。

 

5、Connector的init方法執行完後,會被調用執行startInternal方法。

同理,此方法會調用protocolHandler.start()方法。

而後protocolHandler的start又會調用endpoint.start()方法。

endpoint的start方法在抽象父類AbstractEndpoint中,是個模板方法,實際上調用的是子類NioEndpoint裏面的startInternal()方法。

該方法主要作了:

一、初始化一些屬性

二、根據以前定義的Acceptor和poller的線程數量,來啓動相應數量的Acceptor和poller。

 

其中poller會將請求交給SocketProcessor,而SocketProcessor的職責就是把具體的請求處理過程委派給Handler,handler會執行Processor接口裏的process()方法,進行「對request的解析,包括請求頭、請求行和請求體」。

而這個process()方法是在抽象父類AbstractHttp11Processor裏實現的,

process()方法會先從socket裏讀取http請求數據,並解析請求頭,構造Request對象和Response對象。

 

6、process()方法最後會調用Adapter的service()方法。

Adapter接口只有一個實現類CoyoteAdapter。

service()完成「請求行和請求體的解析」,並把解析出來的信息封裝到Request對象和Response對象中,以後service()便將封裝了Request以及Response對象的Socket傳給Container容器了。

 

傳輸的代碼是:

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

即:先從Connector中獲取service,而後從service中獲取Container。接着再獲取管道,再獲取管道的第一個值Value,最後調用invoke()方法執行請求。

 

、調用mapperListener的start方法。,該方法註冊了已初始化的組件(在上面講的Connector的init時初始化的mapperListener),而後爲這些組件添加監聽器,最後添加容器之間的映射關係。

 

此時Connector的startIntenal就完成了。

 

至此整個Connector啓動完畢,大體的流程圖以下(須要說明的是,下圖中每一個類或接口中所包含的方法我並無全寫出來,只寫了與流程相關的幾個,而且多處地方的子類選擇不是惟一的,我畫的該流程是基於獲取HTTP1.1協議的請求):

 

Container

Container容器是子容器的父接口,全部的子容器都繼承該接口。

Container容器還繼承Lifecycle接口,這樣就擁有了完整的生命週期,他的子容器也就擁有了完整的生命週期。

Container接口的實現子類爲ContainerBase,

其下的四個子容器接口爲:Engine、Host、Context、Wrapper。他們之間是逐層包含的,每一個service只能有一個Engine,一個Engine可以含有多個Host,每一個Host能夠含有多個Context,每一個Context可以含有多個Wrapper。

四個子容器接口也有各自的子實現類,這些實現類繼承自ContainerBase。

 

4中子容器的做用:

一、Engine:接收Connector傳來的request和response,選擇可用的Host來處理當前請求管理Host)。

二、Host:表明一個虛擬主機,也是一個站點。外面以www.demo.com/test這個url爲例,www.demo.com這個域名就表明一個Host,該域名對應的就是webapps目錄所表明的站點。

三、Context:表明一個應用,仍是以www.demo.com/test這個url爲例,經過域名能夠直接訪問到ROOT根目錄裏面的應用就是主應用。webapps/test目錄能夠經過www.demo.com/test訪問到,這個test就又是一個子應用。因而這就是兩個Context(每個應用對應一個Context)。

因此www.demo.com下的應用都屬於該Host站點。而其餘的域名又是其餘Host站點。

四、Wrapper:每個Wrapper封裝一個servlet。

 

這裏須要特殊說明的是Container的四個子容器的生命週期:

一、雖然四個子容器的共同父類ContainerBase定義了initInternal()和startInternal()方法,但子容器仍是能夠根據自身狀況再添加內容。

二、ContainerBase的init()方法是service初始化的時候調用的。但四個子容器的init方法「並非」在ContainerBase的init()方法中被循環調用。而是在執行start()方法時,經過狀態斷定來調用的init()(若是沒初始化就初始化)。

三、Context和Wrapper是「動態添加的」,在站點目錄下每放置一個war包,就會動態添加一個Context,在web.xml裏每配置一個servlet,就能夠動態添加一個Wrapper。因此子容器的start方法不只僅在「tomcat啓動的時候會被調用」,當"父容器ContainerBase添加某個子容器時",也會調用該子容器的start()方法。

 

Container的執行流程爲兩條:

初始化

第一條流程爲:Service執行init()調用Container的init()方法(注意,此時Container並不會調用子容器的init()方法),而後Service執行start()時調用Container的start,Container的start再循環調用每個子容器的start()方法。而後再調用管道的「生命週期管理」(管道不須要初始化,因此在init()中不會調用)。第一條流程的流程圖示以下:

 

Container的init()和start()方法執行內容以下:

1、Container的啓動是經過init()和start()完成的。以前分析過這兩個方法都會經過service來調用。

init()和start存在於LifecycelBean中,實際上最後調用的是ContainerBase中的initInternal()和startInternal()方法。

 

ContainerBase的initInternal方法主要是初始化了ThreadPoolExecutor類(startStopExecutor屬性),用於管理啓動和關閉的線程。

ThreadPoolExecutor繼承自Executor用於管理線程,這裏就不過多介紹了。

代碼以下:

    protected void initInternal() throws LifecycleException {
        LinkedBlockingQueue startStopQueue = new LinkedBlockingQueue();
        this.startStopExecutor = new ThreadPoolExecutor(this.getStartStopThreadsInternal(), this.getStartStopThreadsInternal(), 10L, TimeUnit.SECONDS, startStopQueue, new ContainerBase.StartStopThreadFactory(this.getName() + "-startStop-"));
        this.startStopExecutor.allowCoreThreadTimeOut(true);
        super.initInternal();
    }

 

2、ContainerBase執行完initInternal後,會因爲service的start的調用,執行ContainerBase的startInternal()方法。

代碼以下:

    protected synchronized void startInternal() throws LifecycleException {
        this.logger = null;
        this.getLogger();

 //若是有Cluster和Realm,則調用他們的start方法。
        Cluster cluster = this.getClusterInternal();
        if(cluster != null && cluster instanceof Lifecycle) {
            ((Lifecycle)cluster).start();
        }

        Realm realm = this.getRealmInternal();
        if(realm != null && realm instanceof Lifecycle) {
            ((Lifecycle)realm).start();
        }

        Container[] children = this.findChildren();
        ArrayList results = new ArrayList();

 //使用startStopExecutor調用新線程來啓動每個子容器
        for(int fail = 0; fail < children.length; ++fail) {
            results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[fail])));
        }

        boolean var10 = false;
        Iterator i$ = results.iterator();

        while(i$.hasNext()) {
            Future result = (Future)i$.next();

            try {
                result.get();
            } catch (Exception var9) {
                log.error(sm.getString("containerBase.threadedStartFailed"), var9);
                var10 = true;
            }
        }

        if(var10) {
            throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
        } else {
     //調用管道的啓動方法
            if(this.pipeline instanceof Lifecycle) {
                ((Lifecycle)this.pipeline).start();
            }

     //設置生命週期狀態爲STARTING狀態。
            this.setState(LifecycleState.STARTING);

     //調用threadStart方法啓動後臺線程
            this.threadStart();
        }
    }

startInternal()方法主要作了5件事:

一、若是有Cluster和Realm,則調用他們的start方法。

Cluster用於配置集羣,在server.xml中可配置,它的做用是同步session。

Realm是tomcat的安全域,能夠用來管理資源的訪問權限。

 

二、使用startStopExecutor調用新線程來啓動每個子容器(即調用他們的start()方法),具體啓動過程是經過一個for循環對每一個子容器啓動一個線程(這樣能夠多個線程同時啓動,效率高),並將返回的Future保存到一個List中,而後遍歷每一個Future並調用其get方法。(下面詳細說)

 

三、調用管道中Value的start方法來啓動管道。(下面詳細說)

 

四、設置生命週期狀態爲STARTING狀態。

 

五、調用threadStart方法啓動後臺線程,該線程是一個while循環,按期調用backgroundProcess方法作一些事情,間隔時間可經過屬性設置,單位是秒,若是小於0就不啓動後臺線程了。

backgroundProcess()方法在ContainerBase、StandardContext、StandardWrapper中都有實現。

ContainerBase中的backgroundProcess():調用了Cluster、Realm和管道的backgroundProcess()方法。

StandardContext中的backgroundProcess():調用ContainerBase中的backgroundProcess(),還對Session過時和資源變化進行了處理。

StandardWrapper中的backgroundProcess():調用ContainerBase中的backgroundProcess(),還會對jsp生成的servlet按期進行檢查。

 

至此Container的啓動就完成了,接下來咱們詳細的說一下上面的四個子容器的啓動,和管道的啓動。

 

先來看看使用startStopExecutor調用新線程來啓動每個子容器(即調用他們的start()方法)時,每個子容器都幹了些啥。

因爲每個子容器的接口都繼承自Container接口,而Container接口又繼承自Lifecycle接口,因此每個子容器都有完整的生命週期(都擁有start和init方法),因此調用各個子容器的start方法實際上就是調用他們各自實現類的startInternal()方法。

 

StandardEngine

protected void initInternal() throws LifecycleException {
    this.getRealm();
    super.initInternal();
}

由源碼可看出init方法調用了getRealm(),做用是若是沒有配置Realm,則使用默認的NullPealm。而後調用了父類ContainerBase中的initInternal方法。

protected synchronized void startInternal() throws LifecycleException {
    if(log.isInfoEnabled()) {
        log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo());
    }

    super.startInternal();
}

由源碼可看出:startInternal()只是調用了父類的startInternal()方法。

 

StandardHost

Host的默認實現類StandardHost沒有重寫initInternal方法,因此初始化時調用的是父類的相應方法。

startInternal代碼以下:

protected synchronized void startInternal() throws LifecycleException {
    String errorValve = this.getErrorReportValveClass();
    if(errorValve != null && !errorValve.equals("")) {
        try {
            boolean t = false;
            Valve[] valves = this.getPipeline().getValves();
            Valve[] valve = valves;
            int len$ = valves.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                Valve valve1 = valve[i$];
                if(errorValve.equals(valve1.getClass().getName())) {
                    t = true;
                    break;
                }
            }

            if(!t) {
                Valve var9 = (Valve)Class.forName(errorValve).newInstance();
                this.getPipeline().addValve(var9);
            }
        } catch (Throwable var8) {
            ExceptionUtils.handleThrowable(var8);
            log.error(sm.getString("standardHost.invalidErrorReportValveClass", new Object[]{errorValve}), var8);
        }
    }

    super.startInternal();
}

由此看到此方法就是查看管道中是否有ErrorReportValve,若是沒有就將他添加進去。判斷的方法就是遍歷管道里面的全部value,而後經過每一個value的名字進行判斷。

 

StandardContext

在startInternal中經過一個var25的boolean來判斷是否進行下一步的處理,起始位true,當中途遇到問題後改成false,後面某些處理就不作了。

if(var25 && !this.listenerStart()) {
    log.error(sm.getString("standardContext.listenerFail"));
    var25 = false;
}

此處觸發Listener(Listener定義在web.xml中)。

 

if(var25 && !this.filterStart()) {
    log.error(sm.getString("standardContext.filterFail"));
    var25 = false;
}

此處觸發web.xml中配置的filter過濾器。

 

if(var25 && !this.loadOnStartup(this.findChildren())) {
    log.error(sm.getString("standardContext.servletFail"));
    var25 = false;
}

此處初始化了全部Servlet,即調用在web.xml中配置了load-on-startup的servlet的init方法初始化(load-on-startup的做用是標記容器是否在啓動時加載此servlet)。

 

StandardWrapper

StandardWrapper沒有重寫initInternal方法,其startInternal()源碼以下:

protected synchronized void startInternal() throws LifecycleException {
    Notification notification;
    if(this.getObjectName() != null) {
        notification = new Notification("j2ee.state.starting", this.getObjectName(), (long)(this.sequenceNumber++));
        this.broadcaster.sendNotification(notification);
    }

    super.startInternal();
    this.setAvailable(0L);
    if(this.getObjectName() != null) {
        notification = new Notification("j2ee.state.running", this.getObjectName(), (long)(this.sequenceNumber++));
        this.broadcaster.sendNotification(notification);
    }

}

主要就作了三件事:

一、用broadcaster發送通知,只要用於JMX,

二、調用父類的startInternal方法

三、調用setAvailable方法,做用是設置Wrapper包含的全部servlet的有效的起始時間。

 

下面談論管道的startInternal方法:

管道

由源碼可知管道是在ContainerBase中啓動的,而四個子容器都繼承了ContainerBase,因此四個自容器都有屬於本身的管道。

而每個管道里裝的都是「Value」類型,Value是接口,Value的實現類用於進行各類各樣的具體處理操做。

所謂管道就是指多個處理者(value)對某一個請求「依次」進行處理,請求交給第一個value處理完後,再交給第二個value處理·····,每一條管道的「最後一個Value」都會有點特殊,該Value會將請求發送給下一個管道,而後下一個管道拿到請求後,繼續交給該管道內的第一個value處理·····

舉例,如:

請求到達Engine後,其實是傳給了Engine的管道(由於Engine繼承了Container,因此他擁有StandardPipeline類型的管道),而後管道中有一個Value first屬性,該屬性是一個鏈式結構,存放在管道中first裏面的Value是該管道的第一個value,而該value內部存在指針,指向他的下一個value。

管道就能依靠頭value來遍歷該管道內的每個value,讓他們依次處理請求。

而Engine管道的最後一個value爲StandardEngineValue類,該value會將請求發送給Host的管道。

 

總結一下,每個子容器都有它的StandardPipeline管道,每一條管道中裝有多個value,value中的 invoke方法用來進行具體的處理請求,每一個管道中的最後一個value會將請求傳給下一個子容器的管道。(感受本身有點囉嗦了。。。 (`・ω・´))

 

回到管道的生命週期講解,

在ContainerBase中調用管道的啓動方法(該語句存在於startInternal中):

if(this.pipeline instanceof Lifecycle) {
    ((Lifecycle)this.pipeline).start();
}

Pipeline的實現類爲StandardPipeline類。該類中相應的源碼爲:

protected synchronized void startInternal() throws LifecycleException {
    Valve current = this.first;
    if(current == null) {
        current = this.basic;
    }

    for(; current != null; current = current.getNext()) {
        if(current instanceof Lifecycle) {
            ((Lifecycle)current).start();
        }
    }

    this.setState(LifecycleState.STARTING);
}

首先調用了first屬性賦值給了current,first屬性裏存儲的是「第一個value」,並將它賦值到了當前value上。

以後作了一個判斷,判斷當前value是否爲空,爲空就將當前value設置爲「最後一個value」。

以後從當前value(即:第一個value)開始調用start方法,每調用完一個,就將當前value的下一個value賦值到當前value上。即:循環調用該管道內全部value的start方法。

 

咱們再來看一下value的start方法。

value繼承於生命週期接口,因此調用start就是調用其子類的startInternal()方法。

全部的子value都繼承於ValueBase類,而startInternal方法就是定義在ValueBase類裏,源碼以下:

protected synchronized void startInternal() throws LifecycleException {
    this.setState(LifecycleState.STARTING);
}

能夠看出ValueBase中的startInternal方法只作了一件事,就是將當前狀態設置爲STARTING。

 

至此,第一條流程就完成了。

 

傳值

第二條流程爲:Connector的Adapter將request和response傳給Container的管道,從Engine的管道一路處理到Wrapper的管道,Wrapper再將response一路返回給Engine,而後Engine將response返回給Connector,Connector再返回給用戶瀏覽器。

 

須要提早說明的是,四個自容器的管道中,每一個管道都有多個value用來依次處理請求,但我在這主要分析「每一個子容器管道的最後一個value」,分別是:StandardEngineValue、StandardHostValue、StandardContextValue、StandardWrapperValue。

 

StandardEngineValue

final class StandardEngineValve extends ValveBase {
    private static final StringManager sm = StringManager.getManager("org.apache.catalina.core");

    public StandardEngineValve() {
        super(true);
    }

    public final void invoke(Request request, Response response) throws IOException, ServletException {
        Host host = request.getHost();
        if(host == null) {
            response.sendError(400, sm.getString("standardEngine.noHost", new Object[]{request.getServerName()}));
        } else {
            if(request.isAsyncSupported()) {
                request.setAsyncSupported(host.getPipeline().isAsyncSupported());
            }

            host.getPipeline().getFirst().invoke(request, response);
        }
    }

    public final void event(Request request, Response response, CometEvent event) throws IOException, ServletException {
        request.getHost().getPipeline().getFirst().event(request, response, event);
    }
}

這裏的invoke方法主要就作了一件事:

根據請求找到所對應的站點(Host),而後找到host的管道,調用其頭部value的invoke方法,並把request和response傳給他。

 

StandardHostValue

其invoke源碼爲:

public final void invoke(Request request, Response response) throws IOException, ServletException {
    Context context = request.getContext();
    if(context == null) {
        response.sendError(500, sm.getString("standardHost.noContext"));
    } else {
        if(request.isAsyncSupported()) {
            request.setAsyncSupported(context.getPipeline().isAsyncSupported());
        }

        boolean asyncAtStart = request.isAsync();
        boolean asyncDispatching = request.isAsyncDispatching();

        try {
            context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
            if(!asyncAtStart && !context.fireRequestInitEvent(request)) {
                return;
            }

            try {
                if(asyncAtStart && !asyncDispatching) {
                    if(!response.isErrorReportRequired()) {
                        throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
                    }
                } else {
                    context.getPipeline().getFirst().invoke(request, response);
                }
            } catch (Throwable var10) {
                ExceptionUtils.handleThrowable(var10);
                if(response.isErrorReportRequired()) {
                    this.container.getLogger().error("Exception Processing " + request.getRequestURI(), var10);
                } else {
                    request.setAttribute("javax.servlet.error.exception", var10);
                    this.throwable(request, response, var10);
                }
            }

            response.setSuspended(false);
            Throwable t = (Throwable)request.getAttribute("javax.servlet.error.exception");
            if(!context.getState().isAvailable()) {
                return;
            }

            if(response.isErrorReportRequired()) {
                if(t != null) {
                    this.throwable(request, response, t);
                } else {
                    this.status(request, response);
                }
            }

            if(!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) {
                context.fireRequestDestroyEvent(request);
            }
        } finally {
            if(ACCESS_SESSION) {
                request.getSession(false);
            }

            context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
        }

    }
}

Host的用處再上上面已經講過了,一個host表明一個站點(也叫一個虛擬主機),

其處理過程能夠總結以下:

一、先根據request請求,選擇一個context容器。若是不爲空,則繼續處理。

二、調用Context容器的bind()方法。此方法獲取一條線程,而後讓該線程處理Context。

三、獲取context的管道,再調用管道中頭value的invoke方法,將request和response傳入其中。

 

StandardContextValue

其invoke源碼以下:

public final void invoke(Request request, Response response) throws IOException, ServletException {
    MessageBytes requestPathMB = request.getRequestPathMB();
    if(!requestPathMB.startsWithIgnoreCase("/META-INF/", 0) && !requestPathMB.equalsIgnoreCase("/META-INF") && !requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0) && !requestPathMB.equalsIgnoreCase("/WEB-INF")) {
        Wrapper wrapper = request.getWrapper();
        if(wrapper != null && !wrapper.isUnavailable()) {
            try {
                response.sendAcknowledgement();
            } catch (IOException var6) {
                this.container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), var6);
                request.setAttribute("javax.servlet.error.exception", var6);
                response.sendError(500);
                return;
            }

            if(request.isAsyncSupported()) {
                request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
            }

            wrapper.getPipeline().getFirst().invoke(request, response);
        } else {
            response.sendError(404);
        }
    } else {
        response.sendError(404);
    }
}

其處理過程能夠總結以下:

一、先獲取request的請求路徑,該路徑不能直接訪問WEB-INF或者META-INF目錄下的資源。

二、再根據request,選擇合適的Wrapper。

三、再在response中設置一個sendAcknowledgement(在TCP/IP協議中,若是接收方成功的接收到數據,那麼會回覆一個ACK數據。)

四、獲取Wrapper的管道,再調用管道中頭value的invoke方法,將request和response傳入其中。

 

StandardWrapperValue

每一個Wrapper都封裝着一個servlet,因此Wrapper和servlet有着很深的聯繫。

StandardWrapper裏會加載他表明的servlet並建立實例(即調用它的init方法),但不會調用servlet的service方法。

StandardWrapperValue會調用sertvlet的service方法。

 

其具體執行順序以下,

一、分配一個Servlet實例,由於StandardWrapper負責加載servlet,因此也是從Wrapper中獲取servlet。

try {
    if(!unavailable) {
        servlet = wrapper.allocate();
    }
} catch (UnavailableException var38) {
    this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var38);
    long requestPathMB = wrapper.getAvailable();
    if(requestPathMB > 0L && requestPathMB < 9223372036854775807L) {
        response.setDateHeader("Retry-After", requestPathMB);
        response.sendError(503, sm.getString("standardWrapper.isUnavailable", new Object[]{wrapper.getName()}));
    } else if(requestPathMB == 9223372036854775807L) {
        response.sendError(404, sm.getString("standardWrapper.notFound", new Object[]{wrapper.getName()}));
    }
} catch (ServletException var39) {
    this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), StandardWrapper.getRootCause(var39));
    throwable = var39;
    this.exception(request, response, var39);
} catch (Throwable var40) {
    ExceptionUtils.handleThrowable(var40);
    this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var40);
    throwable = var40;
    this.exception(request, response, var40);
    servlet = null;
}

 

二、執行Servlet相關的全部過濾器:

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

try {
    if(servlet != null && filterChain != null) {
        if(context.getSwallowOutput()) {
            boolean var29 = false;

            try {
                var29 = true;
                SystemLogHandler.startCapture();
                if(request.isAsyncDispatching()) {
                    ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
                    var29 = false;
                } else if(comet1) {
                    filterChain.doFilterEvent(request.getEvent());
                    var29 = false;
                } else {
                    filterChain.doFilter(request.getRequest(), response.getResponse());
                    var29 = false;
                }
            } finally {
                if(var29) {
                    String time = SystemLogHandler.stopCapture();
                    if(time != null && time.length() > 0) {
                        context.getLogger().info(time);
                    }

                }
            }

            String t2 = SystemLogHandler.stopCapture();
            if(t2 != null && t2.length() > 0) {
                context.getLogger().info(t2);
            }
        } else if(request.isAsyncDispatching()) {
            ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
        } else if(comet1) {
            filterChain.doFilterEvent(request.getEvent());
        } else {
            filterChain.doFilter(request.getRequest(), response.getResponse());
        }
    }
}

ApplicationFilterChain能夠看作是一個「過濾器鏈」,StandardWrapperValve中的invoke會先建立該類的實例,而後調用它的doFilter方法,即從該鏈中的第一個過濾器開始調用。若是執行到了最後一個過濾器,就開始調用Servlet的service方法。

 

三、關閉過濾器鏈

if(filterChain != null) {
    if(request.isComet()) {
        filterChain.reuse();
    } else {
        filterChain.release();
    }
}

 

四、通知Wrapper,從新委派處理完畢的servlet。

try {
    if(servlet != null) {
        wrapper.deallocate(servlet);
    }
} catch (Throwable var31) {
    ExceptionUtils.handleThrowable(var31);
    this.container.getLogger().error(sm.getString("standardWrapper.deallocateException", new Object[]{wrapper.getName()}), var31);
    if(throwable == null) {
        throwable = var31;
        this.exception(request, response, var31);
    }
}

 

至此Container第二條流程也完成了。

 

總結出來,簡易流程圖以下:

相關文章
相關標籤/搜索