1. 程式人生 > >深入理解Tomcat系列之四 Engine和Host容器

深入理解Tomcat系列之四 Engine和Host容器

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                       

前言

終於到Container容器了,上面說到Connector把封裝了Request物件以及Response物件的Socket傳遞給了Container容器,那麼在Contianer容器中又是怎麼樣的處理流程呢?在說Container容器之前,有必要對Container容器有一個簡單的瞭解,Container容器是子容器的父介面,所有的子容器都必須實現這個介面,在Tomcat中Container容器的設計是典型的責任鏈設計模式,其有四個子容器:Engine、Host、Context和Wrapper。這四個容器之間是父子關係,Engine容器包含Host,Host包含Context,Context包含Wrapper。

我們在web專案中的一個Servlet類對應一個Wrapper,多個Servlet就對應多個Wrapper,當有多個Wrapper的時候就需要一個容器來管理這些Wrapper了,這就是Context容器了,Context容器對應一個工程,所以我們新部署一個工程到Tomcat中就會新建立一個Context容器。Container容器的處理過程也比較複雜,下面是一個大概的流程:

Container容器處理流程

上面出現了Pipeline與Valve,這兩個物件可以分別理解為管道與管道中閘門,當收到從Connector的請求後,這個請求要通過一個個管道以及管道中一個個的閘門,只有全部通過才能最終被具體的Servlet處理。要注意的是,每一個容器都有自己的管道和閘門,這些管道與閘門都是由容器自身老控制的,所以我們可以看到注入StandardEngineValve等類了。

下面就從Container容器的四個子容器入手,分析每一個容器是怎麼樣處理的:

Engine容器
Engine容器包含Host容器,根據文章第一部分的架構圖,可以知道其管理的容器是Host,Engine是一個介面,其標準實現類是StandardEngine,下面是其類結構圖:

Engine介面
StandardEngine類

注意其中的addChild方法,其型別是Container,但是其實際管理的就是Host容器。Engine容器處理請求的流程可以簡化如下:

Engine容器處理請求流程簡化版

在剛開始的流程圖中呼叫了StandardEngineValve的invoke方法,這個方法的具體實現如何呢?

程式碼清單4-1:

    /**     * Select the appropriate child Host to process this request,     * based on the requested server name.  If no matching Host can     * be found, return an appropriate HTTP error.     *     * @param
request Request to be processed     * @param response Response to be produced     *     * @exception IOException if an input/output error occurred     * @exception ServletException if a servlet error occurred     */
    @Override    public final void invoke(Request request, Response response)        throws IOException, ServletException {        // Select the Host to be used for this Request        Host host = request.getHost();        if (host == null) {            response.sendError                (HttpServletResponse.SC_BAD_REQUEST,                 sm.getString("standardEngine.noHost",                               request.getServerName()));            return;        }        if (request.isAsyncSupported()) {            request.setAsyncSupported(host.getPipeline().isAsyncSupported());        }        // Ask this Host to process this request        host.getPipeline().getFirst().invoke(request, response);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

可以看到這個方法的任務就是選擇可用的Host容器處理當前的請求,選擇Host容器後,就呼叫其invoke方法,所以具體的處理就轉移到了Host容器。

Host容器
Host容器是Engine容器的子容器,上面也說到Host是受Engine容器管理的,就是指一個虛擬主機,比如我們在訪問具體jsp頁面URL中localhost就是一個虛擬主機,其作用是執行多個應用,並對這些應用進行管理,其子容器是Context,而且一個主機還儲存了主機的相關資訊。Host的標準實現類是StandardHost,其閘門實現是StandardHostValve,下面是StandardHost與StandardHostValve的類結構圖:

StandardHost
StandardHostValve

Host容器的處理流程可以簡化如下:

Host容器處理請求過程簡化版

接著我們回到Engine容器的invoke方法,下面是host.getPipeline().getFirst().invoke(request, response)的方法原始碼:

程式碼清單4-2:

    @Override    public final void invoke(Request request, Response response)        throws IOException, ServletException {        // Select the Context to be used for this Request        Context context = request.getContext();        if (context == null) {            response.sendError                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,                 sm.getString("standardHost.noContext"));            return;        }        // Bind the context CL to the current thread        if( context.getLoader() != null ) {            // Not started - it should check for availability first            // This should eventually move to Engine, it's generic.            if (Globals.IS_SECURITY_ENABLED) {                PrivilegedAction<Void> pa = new PrivilegedSetTccl(                        context.getLoader().getClassLoader());                AccessController.doPrivileged(pa);                            } else {                Thread.currentThread().setContextClassLoader                        (context.getLoader().getClassLoader());            }        }        if (request.isAsyncSupported()) {            request.setAsyncSupported(context.getPipeline().isAsyncSupported());        }        // Don't fire listeners during async processing        // If a request init listener throws an exception, the request is        // aborted        boolean asyncAtStart = request.isAsync();         // An async error page may dispatch to another resource. This flag helps        // ensure an infinite error handling loop is not entered        boolean errorAtStart = response.isError();        if (asyncAtStart || context.fireRequestInitEvent(request)) {            // Ask this Context to process this request            try {                context.getPipeline().getFirst().invoke(request, response);            } catch (Throwable t) {                ExceptionUtils.handleThrowable(t);                if (errorAtStart) {                    container.getLogger().error("Exception Processing " +                            request.getRequestURI(), t);                } else {                    request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);                    throwable(request, response, t);                }            }            // If the request was async at the start and an error occurred then            // the async error handling will kick-in and that will fire the            // request destroyed event *after* the error handling has taken            // place            if (!(request.isAsync() || (asyncAtStart &&                    request.getAttribute(                            RequestDispatcher.ERROR_EXCEPTION) != null))) {                // Protect against NPEs if context was destroyed during a                // long running request.                if (context.getState().isAvailable()) {                    if (!errorAtStart) {                        // Error page processing                        response.setSuspended(false);                        Throwable t = (Throwable) request.getAttribute(                                RequestDispatcher.ERROR_EXCEPTION);                        if (t != null) {                            throwable(request, response, t);                        } else {                            status(request, response);                        }                    }                    context.fireRequestDestroyEvent(request);                }            }        }        // Access a session (if present) to update last accessed time, based on a        // strict interpretation of the specification        if (ACCESS_SESSION) {            request.getSession(false);        }        // Restore the context classloader        if (Globals.IS_SECURITY_ENABLED) {            PrivilegedAction<Void> pa = new PrivilegedSetTccl(                    StandardHostValve.class.getClassLoader());            AccessController.doPrivileged(pa);                        } else {            Thread.currentThread().setContextClassLoader                    (StandardHostValve.class.getClassLoader());        }    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

其處理過程可以總結如下:
1. 為特定的請求URL選擇一個Context容器
2. 把Context容器繫結到執行緒中
3. 判斷是否是一個非同步請求
4. 讓Context去處理這個請求
5. Context執行invoke方法,進入管道中,由StandardContextValve(是ContextValve的標準實現類)處理

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述