您的位置:首页 > 运维架构 > Tomcat

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

2019-05-01 09:45 525 查看
                     

前言

终于到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的标准实现类)处理

           
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: