Tomcat学习笔记之Pipeline-Valve

前言

在Container启动一篇中设计到很多关于管道的,这里做一篇详细的介绍。

整体结构

在[Tomcat学习笔记之启动分析(Container)(六)]一文中设计到很多Pipeline-Valve的概念,我们先用一张图来看下其整体流程:


Tomcat学习笔记之Pipeline-Valve_第1张图片
Pipeline-Value流程图

由这张图我们知道管道的设计为每个容器都带来了灵活的机制,可以按照需要对不同容器添加自定义阀进行不同的逻辑处理,并且Tomcat将管道机制做成可配置形式,对于存在的阀只需通过配置文件即可,还可以自定义阀并配置就可在相应作用域内生效。四个容器中每个容器都包含自己的管道对象,管道对象用于存放若干阀门对象,他们都有自己的基础阀,且基础阀是Tomcat默认设置的,一般不可更改之,以免运行时产生问题 。

Pipeline

所有的管道类都会实现org.apache.catalina.Pipeline这个接口,看下这个接口中定义的方法:


Tomcat学习笔记之Pipeline-Valve_第2张图片
Pipeline

一个管道包含多个阀( Valve ),这些阀共分为两类,一类叫基础阀(通过 #getBasic()、#setBasic()方法调用),一类是普通阀(通过#addValve()、#removeValve()调用)。管道都是包含在一个容器当中,所以 API 里还有 #getContainer()和#setContainer()方法。一个管道一般有一个基础阀(通过 #setBasic()方法添加),可以有 0 到多个普通阀(通过#addValve()方法添加)。

Valve

所有的阀类都会实现org.apache.catalina.Valve这个接口,看下这个接口中定义的方法:


Tomcat学习笔记之Pipeline-Valve_第3张图片
Valve

通过#setNext()设置该阀的下一阀,通过 #getNext()返回该阀的下一个阀的引用,#invoke()方法则执行该阀内部自定义的请求处理代码,#backgroundProcess()执行周期性任务,例如重新加载等。

StandardPipeline

Pipeline 的默认实现类是org.apache.catalina.core.StandardPipeline。

  • 属性
   /**
     * 基础阀门
     */
    protected Valve basic = null;
    /**
     * 与此管道关联的Container
     */
    protected Container container = null;
    /**
     * 普通阀
     */
    protected Valve first = null;
  • 相关方法
    • #setBasic()
public void setBasic(Valve valve) {

        //1. 如果新的阀与旧的一样,直接返回
        Valve oldBasic = this.basic;
        if (oldBasic == valve)
            return;

        //2. 停止旧的阀
        if (oldBasic != null) {
            if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
                try {
                    ((Lifecycle) oldBasic).stop();
                } catch (LifecycleException e) {
                    log.error(sm.getString("standardPipeline.basic.stop"), e);
                }
            }
            if (oldBasic instanceof Contained) {
                try {
                    ((Contained) oldBasic).setContainer(null);
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                }
            }
        }

        //3. 启动新的阀
        if (valve == null)
            return;
        if (valve instanceof Contained) {
            ((Contained) valve).setContainer(this.container);
        }
        if (getState().isAvailable() && valve instanceof Lifecycle) {
            try {
                ((Lifecycle) valve).start();
            } catch (LifecycleException e) {
                log.error(sm.getString("standardPipeline.basic.start"), e);
                return;
            }
        }

        //4. 更新阀链,因为基础阀永远在链的末端
        Valve current = first;
        while (current != null) {
            if (current.getNext() == oldBasic) {
                current.setNext(valve);
                break;
            }
            current = current.getNext();
        }

        this.basic = valve;

    }

流程很简单:停止旧阀->启动新阀->更新阀链

  • #addValve()
public void addValve(Valve valve) {

        //1. 校验我们能够添加这个阀
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);

        //2. 启动该阀
        if (getState().isAvailable()) {
            if (valve instanceof Lifecycle) {
                try {
                    ((Lifecycle) valve).start();
                } catch (LifecycleException e) {
                    log.error(sm.getString("standardPipeline.valve.start"), e);
                }
            }
        }

        //3. 如果first为空直接令first=value,并设置next为基础阀;否则将基础阀替换成该阀,并将基础阀添加至末端
        if (first == null) {
            first = valve;
            valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
                if (current.getNext() == basic) {
                    current.setNext(valve);
                    valve.setNext(basic);
                    break;
                }
                current = current.getNext();
            }
        }
        //4. 通知所有的Container监听器
        container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
    }

从上面知道,基础阀永远都被放在末端。#removeValve()方法将阀从普通阀链中移除,跟上面逻辑差不多,这里不贴了。

  • 子容器基础阀的设置
    • StandardEngine
public StandardEngine() {

        super();
        pipeline.setBasic(new StandardEngineValve());
        /* Set the jmvRoute using the system property jvmRoute */
        try {
            setJvmRoute(System.getProperty("jvmRoute"));
        } catch(Exception ex) {
            log.warn(sm.getString("standardEngine.jvmRouteFail"));
        }
        // By default, the engine will hold the reloading thread
        backgroundProcessorDelay = 10;

    }
  • StandardHost
public StandardHost() {
        super();
        pipeline.setBasic(new StandardHostValve());
    }
  • StandardContext
public StandardContext() {
        super();
        pipeline.setBasic(new StandardContextValve());
        broadcaster = new NotificationBroadcasterSupport();
        // Set defaults
        if (!Globals.STRICT_SERVLET_COMPLIANCE) {
            // Strict servlet compliance requires all extension mapped servlets
            // to be checked against welcome files
            resourceOnlyServlets.add("jsp");
        }
    }
  • StandardWrapper
public StandardWrapper() {
        super();
        swValve=new StandardWrapperValve();
        pipeline.setBasic(swValve);
        broadcaster = new NotificationBroadcasterSupport();

    }

ValveBase

Valve的抽象实现类是org.apache.catalina.valves.ValveBase,实现了所有阀的初始化和启动方法,最终的#invoke()方法由各个子类实现:

  • StandardEngineValve
public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        //1. 从request中获取host
        Host host = request.getHost();
        if (host == null) {
            // HTTP 0.9 or HTTP 1.0 request without a host when no default host
            // is defined. This is handled by the CoyoteAdapter.
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        //2. 调用host的普通阀的#invoke()
        host.getPipeline().getFirst().invoke(request, response);
    }
  • StandardHostValve
public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        //1. 从request获取context
        Context context = request.getContext();
        if (context == null) {
            return;
        }
        //省略部分代码。。。
        if (!response.isErrorReportRequired()) {
          //2. 调用context普通阀的invoke方法
            context.getPipeline().getFirst().invoke(request, response);
        }
      //省略部分代码。。。
}
  • StandardContextValve
public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        //1. 禁止在WEB-INF或META-INF下直接访问资源
        MessageBytes requestPathMB = request.getRequestPathMB();
        if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
                || (requestPathMB.equalsIgnoreCase("/META-INF"))
                || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
                || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        //2. request中获取wrapper
        Wrapper wrapper = request.getWrapper();
        if (wrapper == null || wrapper.isUnavailable()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // Acknowledge the request
        try {
            response.sendAcknowledgement();
        } catch (IOException ioe) {
            container.getLogger().error(sm.getString(
                    "standardContextValve.acknowledgeException"), ioe);
            request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        if (request.isAsyncSupported()) {
            request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
        }
        //3. 调用wrapper普通阀的invoke
        wrapper.getPipeline().getFirst().invoke(request, response);
    }
  • StandardWrapperValve
public final void invoke(Request request, Response response)
        throws IOException, ServletException {
            //省略很多代码。。。。
            //1. 分配servlet实例以处理此请求
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
            //2. 创建调用链
            ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
           //3. 调用调用链
            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
          //4. 释放调用链
          if (filterChain != null) {
            filterChain.release();
          }
          //5. 释放已分配的servlet实例
          if (servlet != null) {
                wrapper.deallocate(servlet);
            }

在StandardWrapperValve#invoke()方法中,分配Servlet,创建调用链,最终进入到我们的业务逻辑中。

总结

综上,请求就可以从连接器内一步一步流转到具体Servlet的#service()方法中。
这里可以看出容器内的 Engine、Host、Context、Wrapper 容器组件的实现的共通点:

  • 这些组件内部都有一个成员变量 pipeline ,因为它们都是从ContainerBase类继承来的,pipeline 就定义在这个类中。所以每一个容器内部都关联了一个管道。
  • 都是在类的构造方法中设置管道内的基础阀。
  • 所有的基础阀的实现最后都会调用其下一级容器(直接从请求中获取下一级容器对象的引用,在上面的分析中已经设置了与该请求相关的各级具体组件的引用)的#getPipeline().getFirst().invoke() 方法,直到 Wrapper 组件。因为 Wrapper 是对一个 Servlet 的包装,所以它的基础阀内部调用的过滤器链的 #doFilter()方法和 Servlet 的#service()方法。

你可能感兴趣的:(Tomcat学习笔记之Pipeline-Valve)