tomcat架构分析(valve机制)
Tomcat中按照包含关系一共有四个容器——engine,host,context,wrapper;
这4个容器具体的实现如下
![](https://images2018.cnblogs.com/blog/38654/201808/38654-20180826191802546-42649923.png)
四个容器中每个容器都包含自己的管道对象,管道对象用来存放若干阀门对象(valve ,注意不是value),
但tomcat会为他们制定一个默认的基础阀门【StandardEngineValve,StandardHostValve,StandardContextValve ,StandardWrapperValve】。
四个基础阀门放在各自容器管道的最后一位,用于查找下一级容器的管道在每个容器对象里面都有一个pipeline及valve模块。 它们是容器类必须具有的模块。在容器对象生成时自动产生。
Pipeline就像是每个容器的逻辑总线。在pipeline上按照配置的顺序,加载各个 valve。通过pipeline完成各个valve之间的调用,各个valve实现具体的应用逻辑。
先看一下pipeline及valve的逻辑概念图。
![点击查看原始大小图片](http://dl.iteye.com/upload/picture/pic/124665/28d786ac-5d2a-323b-a46c-219948a42f2f.jpg)
这些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的情况。![](http://dl.iteye.com/upload/picture/pic/124659/9b9843c6-08d1-3812-8a0e-073d151f1b46.jpg)
图中显示的是各个容器默认的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