深入理解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容器的處理過程也比較複雜,下面是一個大概的流程:
上面出現了Pipeline與Valve,這兩個物件可以分別理解為管道與管道中閘門,當收到從Connector的請求後,這個請求要通過一個個管道以及管道中一個個的閘門,只有全部通過才能最終被具體的Servlet處理。要注意的是,每一個容器都有自己的管道和閘門,這些管道與閘門都是由容器自身老控制的,所以我們可以看到注入StandardEngineValve等類了。
下面就從Container容器的四個子容器入手,分析每一個容器是怎麼樣處理的:
Engine容器
Engine容器包含Host容器,根據文章第一部分的架構圖,可以知道其管理的容器是Host,Engine是一個介面,其標準實現類是StandardEngine,下面是其類結構圖:
注意其中的addChild方法,其型別是Container,但是其實際管理的就是Host容器。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的類結構圖:
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的標準實現類)處理