Tomcat 源码分析(二)-请求分析(4)
四、Tomcat 7 阀机制原理
这里的阀机制,就是数据(request)在Tomcat组件之间传递使用的东东。【类似阀门一样的通道的东西】
先看下这个图,Tomcat内的组件数据流转的图↓↓↓↓↓
这个图基本上已经画出了整个的过程,在 Connector 接收到一次连接并转化成请求( Request )后,会将请求传递到 Engine 的管道( Pipeline )的阀( ValveA )中。请求在 Engine 的管道中最终会传递到 Engine Valve 这个阀中。接着请求会从 Engine Valve 传递到一个 Host 的管道中,在该管道中最后传递到 Host Valve 这个阀里。接着从 Host Valve 传递到一个 Context 的管道中,在该管道中最后传递到 Context Valve 中。接下来请求会传递到 Wrapper C 内的管道所包含的阀 Wrapper Valve 中,在这里会经过一个过滤器链( Filter Chain ),最终送到一个 Servlet 中。
这里来分析一下,这里面的管道( Pipeline )和阀( Valve )的操作【流转看上面图的箭头就可以了】
管道和阀 初始化和初次调用
这里管道来流转数据,出现的地方是在:承接上个文章中分析到的CoyoteAdapter类的service 方法中:【44行】
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
这里就开始分析一下这一行的代码:
**connector.getService():**获取当前的 connector 关联的 Service 组件
这里默认的对象是:
org.apache.catalina.core.StandardService
的对象。
**getContainer():**获得的就是里面Engine容器的东东了
这里返回的是:
org.apache.catalina.core.StandardEngine
的对象。这是在Server组件初始化的时候创建的:
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
在 EngineRuleSet 类的 addRuleInstances 方法中的这一段代码:
public void addRuleInstances(Digester digester) { digester.addObjectCreate(prefix + "Engine", "org.apache.catalina.core.StandardEngine", "className"); digester.addSetProperties(prefix + "Engine"); digester.addRule(prefix + "Engine", new LifecycleListenerRule ("org.apache.catalina.startup.EngineConfig", "engineConfigClass")); digester.addSetNext(prefix + "Engine", "setContainer", //已Container方法设置了StandardEngine对象 "org.apache.catalina.Container");
所以这里获取的是:org.apache.catalina.core.StandardEngine对象。
getPipeline(): StandardEngine 类的pipeline 变量
这里就是StandardEngine对象的getPipeline()方法,Pipeline 成员变量。
变量定义在其父类:ContainerBase 类中被定义:
protected Pipeline pipeline = new StandardPipeline(this);
所以 ,这里返回的是
StandardPipeline
类的对象。
这里返回的StandardPipeline就是管道了,之后会详细分析一下的。
**getFirst().invoke(request, response):**这个就是调用管道里面的阀,并执行内部方法流转数据了。
分析管道和阀的概念和实现
**管道Pipeline:**所谓的管道就是实现了org.apache.catalina.Pipeline
这个接口,在上面的new StandardPipeline(this);
这个代码的时候创建的。
这里的Pipeline接口中,最重要的就是
basic:基础阀(通过 getBasic、setBasic 方法调用)
first:第一个阀
vaules:普通阀(通过 addValve、removeValve 调用)
这里需要细说一下addValve方法:
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 (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); }
这里的代码,会给管道添加一个普通阀的时候,已【first】和【basic】作为头尾,维护一个链表结构。
**阀Valve:**实现org.apache.catalina.Valve
这个接口。
接口中主要的定义,就是setNext、getNext、invoke 这三个方法;
通过setNext设置该阀的下一阀,通过 getNext 返回该阀的下一个阀的引用,invoke 方法则执行该阀内部自定义的请求处理代码。
在invoke 方法中通常都会有这段代码:
getNext().invoke(request, response);
,这样就会执行所有的阀了。
具体的数据流转
- 这里从上面分析的getPipeline()返回StandardPipeline类开始;
这个类的初始化时在getContainer()返回的StandardEngine类的初始化方法中定义的:
public class StandardEngine extends ContainerBase implements Engine {
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;
}
。。。。。。
第5行,设置了基础阀。所以
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)
会执行的是 StandardEngineValve类的invoke方法。
- StandardEngineValve.invoke()方法:
final class StandardEngineValve extends ValveBase {
@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);
}
。。。。。。
这个从request中获取到Host对象,【默认是:org.apache.catalina.core.StandardHost
对象】。然后就是第20行,进行获取管道以及执行阀的**invoke()**方法;
- StandardHost类的通道的阀的invoke()方法:
StandardHost 的构造方法的实现:
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
StandardHostValve类的invoke()方法:
/**选择适当的子上下文来处理此请求,
基于指定的请求URI。如果没有匹配的上下文可以
如果找到,则返回相应的HTTP错误。**/
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext(); //获取到Context 组件对象
//。。。一些判断。。。。。
if (asyncAtStart || context.fireRequestInitEvent(request.getRequest())) {
try {
if (!asyncAtStart || asyncDispatching) {
//下↓↓↓↓↓↓↓↓面这里看看就可以了
context.getPipeline().getFirst().invoke(request, response);
//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
} else {
if (!response.isErrorReportRequired()) {
throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
}
}
} catch (Throwable t) {
// 。。。。。。。。
}
}
// 。。。。。。。。
//现在请求/响应对回到容器下
//控制提升悬架,以便错误处理可以
//完成和/或容器可以刷新任何剩余数据
}
这里可以看到Host【StandardHostValve】中流转,接下来会流转到Context,也就是org.apache.catalina.core.StandardContext
类中的管道去。
- StandardContext类的通道的阀的invoke()方法:
管道在StandardContext构建的时候创建:
public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve()); // 就是这个 设置了当前的管道
broadcaster = new NotificationBroadcasterSupport();
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
resourceOnlyServlets.add("jsp");
}
}
这里的阀是:StandardContextValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
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;
}
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
try {
response.sendAcknowledgement();
} catch (IOException ioe) {
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response); //要看的就只是这个
}
这里,基础阀的invoke方法:是调用wrapper容器的管道进行数据流转。
wrapper 对象默认是org.apache.catalina.core.StandardWrapper
类的实例
- StandardWrapper类的通道的阀的invoke()方法:
设置基础阀:
public StandardWrapper() {
super();
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
然后就是StandardWrapperValve这个阀的invoke 方法;
这个代码 戝长。。。。。不去copy了 //╮(╯_╰)╭#\\
它的方法备注是酱紫的:
Invoke the servlet we are managing, respecting the rules regarding
servlet lifecycle and SingleThreadModel support.
调用我们正在管理的servlet,遵守有关
servlet生命周期和singlethreadmodel支持。
意思就是:
在这里最终会调用请求的 url 所匹配的 Servlet 相关过滤器( filter )的 doFilter 方法及该 Servlet 的 service 方法(这段实现都是在过滤器链 ApplicationFilterChain 类的 doFilter 方法中)
- 数据流转结束
到这里,已经到了Tomcat 处理组件的最里层了。Wrapper 是对一个 Servlet 的包装,所以他的基础阀内部调用的是过滤链doFiter方法 和 Servlet 的 service 方法;
关于数据内部传递小结
请求在service组件,完成请求分析组装成内部对象request和response之后,就根据组件之间的管道和阀进行数据传递和处理链的传递。
request对象在组装的时候已经适配了对应的组件,在《请求与容器中具体组件的匹配》的时候已经说明。组件内部的管道在初始化之后,会设置基础阀–基础阀的invoke方法会调用下一个组件的管道,xxxxxxxx.getPipeline().getFirst().invoke(request, response);
进行数据处理的传递。
处理链的顺序是被固定的。
管道的处理会从First阀一直执行到基础阀。
这样子,数据和处理链就会一直传递处理到Wrapper 组件,也就是具体的一个Servlet 。
到这里Socket连接请求在Tomcat容器内的处理、传递就完成了。
之后的的数据和处理就到了 web 应用里面了
参考资料
小杭 2019-04-18
终于看完这一部分了 (:з」∠)
这一部分的,学习了好几个月 ε=(´ο`*)))唉