tomcat架构分析(valve机制)

Tomcat中按照包含关系一共有四个容器——engine,host,context,wrapper; 
这4个容器具体的实现如下

四个容器中每个容器都包含自己的管道对象,管道对象用来存放若干阀门对象(valve ,注意不是value),

但tomcat会为他们制定一个默认的基础阀门【StandardEngineValve,StandardHostValve,StandardContextValve ,StandardWrapperValve】。

四个基础阀门放在各自容器管道的最后一位,用于查找下一级容器的管道在每个容器对象里面都有一个pipeline及valve模块。 它们是容器类必须具有的模块。在容器对象生成时自动产生。
 
Pipeline就像是每个容器的逻辑总线。在pipeline上按照配置的顺序,加载各个 valve。通过pipeline完成各个valve之间的调用,各个valve实现具体的应用逻辑。 

先看一下pipeline及valve的逻辑概念图。 

 
这些valve就是在tomcat的server.xml中配置,只要满足一定条件,继承ValveBase基类 
 
public class TestHandlerValve extends ValveBase {

    private TestManager manager;
    public void setTestManager(TestManager manager) {
        this.manager = manager;
    }

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        try {
            // 调用servlet之前的操作

            getNext().invoke(request, response);

            // 调用servlet之后的操作
        } finally {
            manager.afterRequest();
        }
    }
}

就可以在不同的容器中配置,然后在消息流中被逐一调用。每个容器的valve的作用域不一样,在总体结构中已有说明。这里红色标记的是配置的自定义的valve,这样可以扩展成多个其他应用,例如cluster应用等。 

Tomcat实现 

Tomcat提供了Pipeline的标准实现: org.apache.catalina.core.StandardPipeline


四大容器类StandardEngine,StandardHost,StandardContext及StandardWrapper都有各自缺省的标准valve实现。它们分别是 
  • Engine:org.apache.catalina.core.StandardEngineValve
  • Host: org.apache.catalina.core.StandardHostValve
  • Context:org.apache.catalina.core.StandardContextValve
  • Wrapper:org.apache.catalina.core.StandardWrapperValve

容器类生成对象时,都会生成一个pipeline对象,同时,生成一个缺省的valve实现,并将这个标准的valve对象绑定在其pipeline对象上。以StandardHost类为例: 
public class StandardHost extends ContainerBase implements Host {   
  
  protected Pipeline pipeline = new StandardPipeline(this);   
  public StandardHost() {   
    super();   
    pipeline.setBasic(new StandardHostValve());   
  }   
  
}  

Valve实现了具体业务逻辑单元。可以定制化valve(实现特定接口),然后配置在server.xml里。每层容器都可以配置相应的 valve,当只在其作用域内有效。

例如engine容器里的valve只对其包含的所有host里的应用有效。定制化的valve是可选的,但是每个容 器有一个缺省的valve,例如engine的StandardEngineValve,是在StandardEngine里自带的,它主要实现了对其子 host对象的StandardHostValve的调用,以此类推。 
配置的例子有:  
<Engine name="Catalina" defaultHost="localhost">  
  <Valve className="MyValve0"/>  
  <Valve className="MyValve1"/>  
  <Valve className="MyValve2"/>  
   ……  
  <Host name="localhost"  appBase="webapps">  
  </Host>  
</Engine>  

当在server.xml文件中配置了一个定制化valve时,会调用pipeline对象的addValve方法,将valve以链表方式组织起来,看一下代码; 

@Override
    public void addValve(Valve valve) {

        // Validate that we can add this Valve
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);

        // Start the new component if necessary
        if (getState().isAvailable()) {
            if (valve instanceof Lifecycle) {
                try {
                    ((Lifecycle) valve).start();
                } catch (LifecycleException e) {
                    log.error("StandardPipeline.addValve: start: ", e);
                }
            }
        }

        // Add this Valve to the set associated with this Pipeline
        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();
            }
        }

        container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
    }

从上面可以清楚的看出,valve按照容器作用域的配置顺序来组织valve,每个valve都设置了指向下一个valve的next引用。同时,每个容器缺省的标准valve都存在于valve链表尾端,这就意味着,在每个pipeline中,缺省的标准valve都是按顺序,最后被调用。 

消息流 

先看一下四大容器的标准valve的调用逻辑图。从中可以梳理出标准valve的逻辑。注意此图只是在缺省配置下的状态,也就是说每个pipeline只包含一个标准valve的情况。 
 
图中显示的是各个容器默认的valve之间的实际调用情况。从StandardEngineValve开始,一直到 StandardWrapperValve,完成整个消息处理过程。
StandardEngineValve的invoke的方法定义如下,
    @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);
    }

这个方法会调用 StandardHost 的第一个valve来执行。

 
注意每一个上层的valve都是在调用下一层的valve返回后再返回的,这样每个上 层valve不仅具有request对象,同时还能拿到response对象,每一层有多个valve,
 
以Engine层为例, 都是以这个顺序 valve0,valve1,...StandardEngineValve进行调用,典型的责任链模式,各个valve之间根据一定的逻辑通过 getNext().invoke(request, response);调用下一个valve
 
 
 
posted @ 2016-11-19 23:56  南极山  阅读(1299)  评论(0编辑  收藏  举报