前言
在Container启动一篇中设计到很多关于管道的,这里做一篇详细的介绍。
整体结构
在[Tomcat学习笔记之启动分析(Container)(六)]一文中设计到很多Pipeline-Valve的概念,我们先用一张图来看下其整体流程:
Pipeline-Value流程图
由这张图我们知道管道的设计为每个容器都带来了灵活的机制,可以按照需要对不同容器添加自定义阀进行不同的逻辑处理,并且Tomcat将管道机制做成可配置形式,对于存在的阀只需通过配置文件即可,还可以自定义阀并配置就可在相应作用域内生效。四个容器中每个容器都包含自己的管道对象,管道对象用于存放若干阀门对象,他们都有自己的基础阀,且基础阀是Tomcat默认设置的,一般不可更改之,以免运行时产生问题 。
Pipeline
所有的管道类都会实现org.apache.catalina.Pipeline这个接口,看下这个接口中定义的方法:
Pipeline
一个管道包含多个阀( Valve ),这些阀共分为两类,一类叫基础阀(通过 #getBasic()、#setBasic()方法调用),一类是普通阀(通过#addValve()、#removeValve()调用)。管道都是包含在一个容器当中,所以 API 里还有 #getContainer()和#setContainer()方法。一个管道一般有一个基础阀(通过 #setBasic()方法添加),可以有 0 到多个普通阀(通过#addValve()方法添加)。
Valve
所有的阀类都会实现org.apache.catalina.Valve这个接口,看下这个接口中定义的方法:
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()方法。
网友评论