参考资料:
《Tomcat组成与工作原理》
《Tomcat - Container的管道机制:责任链模式》
《Tomcat源码解析系列 Pipeline 与 Valve》
前文:
《Tomcat源码:启动类Bootstrap与Catalina的加载》
《Tomcat源码:容器的生命周期管理与事件监听》
《Tomcat源码:StandardServer与StandardService》
《Tomcat源码:Container接口》
《Tomcat源码:StandardEngine、StandardHost、StandardContext、StandardWrapper》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
前言
在前文中,我们介绍了tomcat容器部分中的Engine、Host、Context、Wrapper,截止Wrapper中loadOnStartup=1的servelt启动后整个tomcat的启动就算完成了,不过除了容器tomcat还有连接器的部分,即如何将请求发给对应的servlet来进行处理。连接器的内容我们会在后续的文章中进行介绍。
本文我们来介绍下容器中最后的部分内容,即Pipeline 与 Valve,这两个组件也属于容器,不过他们的作用不是提供servlet服务,而是实现请求在各级容器中的传递,属于容器中的“连接器”。
目录
前言
一、Pipeline 与 Valve的启动
1、StandardPipeline
1.1、生命周期方法
1.2、Valve管理方法
2、Valve
二、Pipeline与Valve传递请求
1、StandardEngineValve
2、StandardHostValve
3、StandardContextValve
4、StandardWrapperValve
一、Pipeline 与 Valve的启动
1、StandardPipeline
在抽象类ContainerBase中定义了成员变量Pipeline,其实现类为StandardPipeline
// ContainerBase.java
protected final Pipeline pipeline = new StandardPipeline(this);
,由于ContainerBase是我们上文所讲的Engine、Host、Context、Wrapper容器的公共父类,所以这些容器都会有一个成员变量Pipeline。
1.1、生命周期方法
Pipeline同样继承了抽象类LifecycleBase,因此也实现了Lifecycle接口的生命周期的方法
public class StandardPipeline extends LifecycleBase implements Pipeline{
// ...
}
其中initInternal为空方法,而startInternal则用于启动另一个组件Valve,通过下面代码中的current = current.getNext();我们可以猜出Valve是类似于链表状的结构,这里的startInternal其实就是依次调用这个链表结构中的每个Valve的start方法。
protected void initInternal() {
// NOOP
}
protected synchronized void startInternal() throws LifecycleException {
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle) {
((Lifecycle) current).start();
}
current = current.getNext();
}
setState(LifecycleState.STARTING);
}
1.2、Valve管理方法
在Pipeline中有两个Valve的成员变量first与basic分别表示上面所说的Valve组成的链表结构的头尾节点,其结构如下图。
链表中每个节点的下一节点由每个Valve节点自己保存,可以通过getNext来获取。Valve的更多相关内容我们会在下文介绍,这里先继续看下Pipeline的另外两个方法。
首先是setBasic,从Engine到Wrapper的每个容器在构造方法中都会调用改方法,可以看出来这个方法是为了给StandardPipeline中的basic变量赋值,并且每个容器传入的Valve的实现类都不相同,可以从类名看出其具体类别与容器的实现类相关。
从setBasic的简化内容来看当basic变量为空时直接赋值,如果不为空则操作过程和链表一样先遍历到其前面一个节点,断开连接并将新的basic变量接在最后面。
// StandardPipeline.java
protected Valve basic = null;
public void setBasic(Valve valve) {
Valve oldBasic = this.basic;
// ...
Valve current = first;
while (current != null) {
if (current.getNext() == oldBasic) {
current.setNext(valve);
break;
}
current = current.getNext();
}
this.basic = valve;
}
// StandardEngine.java
public StandardEngine() {
pipeline.setBasic(new StandardEngineValve());
}
// StandardHost.java
public StandardHost() {
pipeline.setBasic(new StandardHostValve());
}
// StandardContext.java
public StandardContext() {
pipeline.setBasic(new StandardContextValve());
}
// StandardWrapper.java
public StandardWrapper() {
swValve = new StandardWrapperValve();
pipeline.setBasic(swValve);
}
然后是addValve方法,从简化的内容来看,除了basic外第一个加入的节点会成为first,第二个加入的会成为second,但basic不会变化,始终都会在最后。
protected Valve first = null;
public void addValve(Valve valve) {
// ...
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();
}
}
}
2、Valve
Valve在每个容器中的实现都不相同,对应我们前文中介绍容器的每个类中的实现分别为StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve。
这四个实现类都继承于抽象类ValveBase,且均未实现生命周期方法,因此都是直接使用的父类ValveBase中的实现。可以从面的代码中看出initInternal、startInternal并未实现什么具体的操作,backgroundProcess则直接是空方法。这是因为Valve和Pipline一样虽然也属于容器但主要职责却是为连接请求提供转发服务,属于容器中的“连接器”。
protected void initInternal() throws LifecycleException {
// 调用父类LifecycleMBeanBase的initInternal方法
// 内容为注册JMX
super.initInternal();
containerLog = getContainer().getLogger();
}
protected synchronized void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
}
public void backgroundProcess() {
// NOOP by default
}
二、Pipeline与Valve传递请求
后面我们会介绍连接器中的CoyoteAdapter的内容,该类的asyncDispatch方法(即请求分发方法)中有如下内容。
// CoyoteAdapter#asyncDispatch
connector.getService().getContainer().
getPipeline().getFirst().invoke(
request, response);
// ContainerBase.java
protected final Pipeline pipeline = new StandardPipeline(this);
public Pipeline getPipeline() {
return this.pipeline;
}
// StandardPipeline.java
protected Valve first = null;
public void addValve(Valve valve) {
// ...
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();
}
}
}
getService即获取StandardService,Service的container即Engine容器。getPipeline则是直接复用的抽象父类ContainerBase 中的实现,内容很明确就是获取成员变量pipline。然后是getFirst,结合上文中的描述,这里是获取的Valve链表结构的first节点,如果first节点为空则转而获取basic节点,然后调用其invoke方法。 (getFirst获取的必然是Valve链表的第一个节点,之所以这么说是因为如果链表中没有first那么basic就是第一个)
下文我们以每个容器中默认的Valve作为切入点介绍下invoke方法,由于该方法中的具体内容需要结合后续的连接器的源码理解,所以暂时只做一些简单的介绍,详细的内容会在后续介绍完连接器后做分析。
1、StandardEngineValve
StandardEngine中的实现为StandardEngineValve,该类中的invoke方法首先获取当前请求中的Host对象,如果没有则直接返回。否则将继续如同上文一样调用host中setbasic时创建的Valve的invoke方法。
// StandardEngine.java
public StandardEngine() {
pipeline.setBasic(new StandardEngineValve());
}
// StandardEngineValve.java
public void invoke(Request request, Response response) throws IOException, ServletException {
// 获取一个 Host 对象,获取不到就直接返回
Host host = request.getHost();
if (host == null) {
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
host.getPipeline().getFirst().invoke(request, response);
}
2、StandardHostValve
StandardHostValve中的invoke也和上文类似,获取当前请求中的context对象,如果没有则直接返回。否则将继续如同上文一样调用context中setbasic时创建的Valve的invoke方法。
public void invoke(Request request, Response response) throws IOException, ServletException {
Context context = request.getContext();
if (context == null) {
if (!response.isError()) {
response.sendError(404);
}
return;
}
// 其余代码
context.getPipeline().getFirst().invoke(request, response);
}
3、StandardContextValve
StandardContextValve继续调用下一个子容器wrapper中的invoke方法。
public void invoke(Request request, Response response) throws IOException, ServletException {
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 其余代码
wrapper.getPipeline().getFirst().invoke(request, response);
}
4、StandardWrapperValve
StandardWrapperValve是整个调用链的最后一环,在这里会调用wrapper.allocate();来获取一个Servlet实例,并调用ApplicationFilterChain#doFilter 方法来处理请求,由于涉及到连接器的内容,这里我们暂时略过,后续等介绍完了连接器我们再回来做具体分析。
public void invoke(Request request, Response response) throws IOException, ServletException {
boolean unavailable = false;
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable", wrapper.getName()));
checkWrapperAvailable(response, wrapper);
unavailable = true;
}
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
}
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
}
// 其余代码
}
整个流程如下图所示,StandardEngineValve、StandardHostValve、StandardContextValve这三个 Valve 的 invoke 方法的核心逻辑就是调用子容器的 Pipeline 的 Valve 的invoke 方法,也就是 StandardEngineValve#invoke -> StandardHostValve#invoke -> StandardContextValve#invoke -> StandardWrapper#invoke 方法。
而 StandardWrapper#invoke 最终调用 ApplicationFilterChain#doFilter 方法来处理请求。
注意:有些文章里介绍说请求会通过getNext遍历子容器中的VAalve链表,但实际上并没有,这里只会调用getFirst来获取子容器中Valve链表的第一个节点(之所以必然是第一个是因为链表中如果没有first那么basic就是第一个)来触发invoke方法。
本文分析了 Pipeline 和 Valve 的相关内容,这两个组件真正起作用的时候是在 Connector 使用容器 Container 处理请求的时候,Connector 会找到自己关联的 Service 的里的 Container 对象(也就是 Engine 对象),然后获取这个对象的 Pipeline,通过这个 Pipeline 对象获取 Pipeline 对象的 Valve 对象,最后通过调用 Valve 对象的 invoke 方法来处理请求。