天天看點

tomcat源碼分析-http請求在Container中的執行路線

  在coyoteadapter的service方法中,主要幹了2件事:

    1. org.apache.coyote.request -> org.apache.catalina.connector.request extends httpservletrequest

        org.apache.coyote.response -> org.apache.catalina.connector. response extends httpservletresponse

        context和wrapper定位

    2. 将請求交給standardenginevalue處理

tomcat源碼分析-http請求在Container中的執行路線

public void service(org.apache.coyote.request req,  

                        org.apache.coyote.response res) {  

    //  

    postparsesuccess = postparserequest(req, request, res, response);  

    connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response);  

}  

postparserequest方法的代碼片段

tomcat源碼分析-http請求在Container中的執行路線

connector.getmapper().map(servername, decodeduri, version,  

                                      request.getmappingdata());  

request.setcontext((context) request.getmappingdata().context);  

request.setwrapper((wrapper) request.getmappingdata().wrapper);  

request通過uri的資訊找到屬于自己的context和wrapper。而這個mapper儲存了所有的容器資訊,不記得的同學可以回到connector的startinternal方法中,最有一行代碼是mapperlistener.start()。在mapperlistener的start()方法中,

tomcat源碼分析-http請求在Container中的執行路線

public void startinternal() throws lifecycleexception {  

    setstate(lifecyclestate.starting);  

    finddefaulthost();  

    engine engine = (engine) connector.getservice().getcontainer();  

    addlisteners(engine);  

    container[] conhosts = engine.findchildren();  

    for (container conhost : conhosts) {  

        host host = (host) conhost;  

        if (!lifecyclestate.new.equals(host.getstate())) {  

            registerhost(host);  

        }  

    }  

在容器初始化和變化時都會觸發監聽事件,進而将所有容器資訊儲存在mapper中。之是以叫mapper,因為它的主要作用就是定位wrapper,而我們在web.xml裡也配了filter/servlet-mapping。

    另外,由上面的代碼可知,在随後的請求路線中,engine可有connector擷取,context和wrapper可直接由request擷取,host也可由request擷取。

tomcat源碼分析-http請求在Container中的執行路線

public host gethost() { return ((host) mappingdata.host); }  

    上面的代碼中還涉及到了兩個很重要的概念--pipeline和value,我們不妨先一睹container的調用鍊和時序圖。

tomcat源碼分析-http請求在Container中的執行路線
tomcat源碼分析-http請求在Container中的執行路線

     對于每個引入的http請求,連接配接器都會調用與其關聯的servlet容器的invoke方法。然後,servlet容器會調用其所有子容器的invoke方法。為什麼必須要有一個host容器呢?

    在tomcat的實際部署中,若一個context執行個體使用contextconfig對象進行設定,就必須使用一個host對象,原因如下:

    使用contextconfig對象需要知道應用程式web.xml檔案的位置,在其webconfig()方法中會解析web.xml檔案

tomcat源碼分析-http請求在Container中的執行路線

// parse context level web.xml  

inputsource contextwebxml = getcontextwebxmlsource();  

parsewebxml(contextwebxml, webxml, false);  

在getcontextwebxmlsource方法裡

tomcat源碼分析-http請求在Container中的執行路線

// servletcontext即core包下的applicationcontext  

url = servletcontext.getresource(constants.applicationwebxml);  

在getresource方法裡

tomcat源碼分析-http請求在Container中的執行路線

string hostname = context.getparent().getname();  

 是以,除非你自己實作一個contextconfig類,否則,你必須使用一個host容器。

    管道(pipeline)包含該servlet容器将要調用的任務。一個閥(value)表示一個具體的執行任務。在servlet容器的管道中,有一個基礎閥,但是,可以添加任意數量的閥。閥的數量指的是額外添加的閥數量,即不包括基礎閥。有意思的是,可以通過server.xml來動态添加閥。

    管道和閥的工作機制類似于servlet程式設計中的過濾器鍊和過濾器,tomcat的設計者采用的是連結清單資料結構來實作的鍊條機制,引入了一個類叫valuecontext。值得注意的是,基礎閥總是最後執行。

    請求最終會被引導到standardwrapper,本人也是首先從wrapper這一層來入手container的,直接看standardwrappervalue的invoke方法

tomcat源碼分析-http請求在Container中的執行路線

@override  

public final void invoke(request request, response response) {  

    requestcount++;  

    standardwrapper wrapper = (standardwrapper) getcontainer();  

    servlet servlet = null;  

    context context = (context) wrapper.getparent();  

    // allocate a servlet instance to process this request  

    try {  

       if (!unavailable) {  

          servlet = wrapper.allocate();  

       }  

    } catch (exception e) {}  

    // create the filter chain for this request  

    applicationfilterfactory factory =  

            applicationfilterfactory.getinstance();  

    applicationfilterchain filterchain =  

            factory.createfilterchain(request, wrapper, servlet);  

    // call the filter chain for this request  

    // note: this also calls the servlet's service() method  

    filterchain.dofilter(request.getrequest(), response.getresponse());  

    // release the filter chain (if any) for this request  

    if (filterchain != null) filterchain.release();  

    // deallocate the allocated servlet instance  

    if (servlet != null) wrapper.deallocate(servlet);  

上面代碼中最重要的三處邏輯就是servlet執行個體的擷取與解除安裝和filter鍊調用。我們先看解除安裝servlet執行個體的代碼

tomcat源碼分析-http請求在Container中的執行路線

    public void deallocate(servlet servlet) throws servletexception {  

        // unlock and free this instance  

        synchronized (instancepool) {  

            countallocated.decrementandget();  

            instancepool.push(servlet);  

            instancepool.notify();  

我們不考慮singlethreadmodel模型,因為較新版本的tomcat已經不用這種模型了(隻有很老的版本才用),顯然,通過上面的代碼可以知道,常用的是線程池模型。下面給出加載servlet執行個體的代碼

tomcat源碼分析-http請求在Container中的執行路線

public servlet allocate() throws servletexception {  

      instance = loadservlet();  

      initservlet(instance);  

      synchronized (instancepool) {  

            while (countallocated.get() >= ninstances) {  

                // allocate a new instance if possible, or else wait  

                if (ninstances < maxinstances) {  

                    try {  

                        instancepool.push(loadservlet());  

                        ninstances++;  

                    } catch (throwable e) {  

                    }  

                } else {  

                        instancepool.wait();  

                    } catch (interruptedexception e) {  

                        // ignore  

                }  

            }  

            countallocated.incrementandget();  

            return instancepool.pop();  

最後,我們來看看filterchain的執行,

tomcat源碼分析-http請求在Container中的執行路線

    public void dofilter(servletrequest request, servletresponse response)  

        throws ioexception, servletexception {  

    internaldofilter(request,response);  

private void internaldofilter(servletrequest request,   

                                  servletresponse response)  

    // call the next filter if there is one  

    if (pos < n) {  

        filter.dofilter(request, response, this);  

     // we fell off the end of the chain -- call the servlet instance  

     servlet.service(request, response);  

顯然,在調用web.xml裡配的某個servlet時,都會先依次調用在web.xml裡配的filter,這可謂是責任鍊設計模式的一種經典實作。

    好了,現在你可以把前文中connector執行過程和本文的container執行過程結合起來了。我始終相信,深入一點,你會更快樂。

原文連結:[http://wely.iteye.com/blog/2295240]