zoukankan      html  css  js  c++  java
  • Tomcat 核心组件 Container容器相关

    Tomcat


    前言

    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容器处理请求的流程可以简化如下:
    StandardEngine1

    在刚开始的流程图中调用了StandardEngineValve的invoke方法,这个方法的具体实现如何呢?

        /**
         * 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);
        }

    可以看到这个方法的任务就是选择可用的Host容器处理当前的请求,选择Host容器后,就调用其invoke方法,所以具体的处理就转移到了Host容器。


    Host容器

    Host容器是Engine容器的子容器,上面也说到Host是受Engine容器管理的,就是指一个虚拟主机,比如我们在访问具体jsp页面URL中localhost就是一个虚拟主机,其作用是运行多个应用,并对这些应用进行管理,其子容器是Context,而且一个主机还保存了主机的相关信息。Host的标准实现类是StandardHost,其闸门实现是StandardHostValve,下面是StandardHost与StandardHostValve的类结构图:

    StandardHost

    StandardHostValue

    Host容器的处理流程可以简化如下:
    StandardHostValue

    接着我们回到Engine容器的invoke方法,下面是host.getPipeline().getFirst().invoke(request, response)的方法源码:

        @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. 为特定的请求URL选择一个Context容器
    2. 把Context容器绑定到线程中
    3. 判断是否是一个异步请求
    4. 让Context去处理这个请求
    5. Context执行invoke方法,进入管道中,由StandardContextValve(是ContextValve的标准实现类)处理



    原文博主:rhwayfunn

  • 相关阅读:
    Mysql权限控制
    Linux查看端口
    linus 下redis守护进程启动
    pymongo创建索引
    mongo批量操作存在更新否则插入
    梯度下降推导过程资料整理
    [转]mitmproxy套件使用攻略及定制化开发
    终极利器!利用appium和mitmproxy登录获取cookies
    how-to-pass-a-class-variable-to-a-decorator-inside-class-definition
    python进阶之魔法函数
  • 原文地址:https://www.cnblogs.com/aixing/p/13327569.html
Copyright © 2011-2022 走看看