tomcat + spring mvc 原理(四):tomcat网络请求的监控与处理2
前言:
原理(三)主要讲了Connector中的容器结构、容器配置和请求监听和请求到达后在Connector中的流转,直到请求进入Container的处理边界。这篇主要是接着原理(三)继续,一直到请求投送到Servlet,之后就属于spring mvc的领域。因为目前主要关于tomcat的原理,之后spring mvc的内容会整合在另一部分,所以就不再向下。
Container传递请求的基本原理
在讲请求如何在Container中流转之前,先要讲一个tomcat中比较重要的Pipeline管道的实现,因为还记的原理(三)的结尾吗?
connector.getService().getContainer().getPipeline()
.getFirst().invoke(request, response);
是的,在Container中,后续请求的处理就是利用的Pipeline管道。
Pipeline实现采用的是责任链模式。责任链模式在GoF的经典设计模式一书中被归为行为型,一个消息被发送之后,整个过程中会有多个处理者依次接收这个消息,对这个消息进行处理。无论是否处理,接收到消息的处理者最后都需要将这个消息返回,交由下一个处理者继续处理。
Pipeline是一个接口,标准实现是StandardPipeline类。
public class StandardPipeline extends LifecycleBase implements Pipeline {
/**
* 基本的消息处理器Value
*/
protected Valve basic = null;
/**
* Pipeline中的注册的第一个消息处理器Value
*/
protected Valve first = null;
/**
* 和Pipeline相关的容器
*/
protected Container container = null;
public StandardPipeline(Container container) {
super();
setContainer(container);
}
.....
}
其中Value是处理者的类型,实现对消息的处理和后续传递。basic是用来存储基本的消息处理器,first用来存储处理者责任链的第一个消息处理器。先看下Value接口的标准抽象实现ValueBase:
public abstract class ValveBase extends LifecycleMBeanBase implements Contained, Valve {
protected Valve next = null;
@Override
public Valve getNext() {
return next;
}
@Override
public void setNext(Valve valve) {
this.next = valve;
}
/**
* 和这个Value相关的容器
*/
protected Container container = null;
@Override
public Container getContainer() {
return container;
}
@Override
public void setContainer(Container container) {
this.container = container;
}
}
由源码可以看出,Value的实现实际上就是一个单向的链表,由next存储下一个节点的引用。这样在一个Value处理完消息后,可以调用getNext()获取下一个节点继续处理。也可以通过setNext()为责任链添加新的Value节点。消息的处理是Value接口中定义了一个invoke方法:
public void invoke(Request request, Response response)
throws IOException, ServletException;
这个是由具体的子类继承ValveBase后自行实现的。
Container传递请求的具体实现
想必你已经注意到了无论是StandardPipeline还是ValveBase中都有一个Container的对象引用,这是因为tomcat中针对Engine、Host、Context和Wrapper都有自己的Pipeline实现。这个结构类似于这样:
现在以Engine的为例,介绍在各种Container中是如何实现消息传递和处理的。首先在ContainerBase中包含一个域对象:
protected final Pipeline pipeline = new StandardPipeline(this);
由于Engine, Host, Context和Wrapper都继承自ContainerBase(原理(二)),所以每个实例都包含一个Pipeline对象引用。在构造函数中会构造这个Pipeline对象,在initInternal()和startInternal()中,也会调用Pipeline的相应初始化和启动方法。根据上面贴出的StandardPipeline的实现,可知 Value first是整个Engine中责任链的起点,而如果first=null,这时候会获取basic的值,处理消息。如果链中不止一个处理器Value,使用addValue()添加时,basic的值会始终放在链表的最后一个节点。代码实现如下:
public class StandardPipeline extends LifecycleBase implements Pipeline {
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}
@Override
public void addValve(Valve valve) {
······
// 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);
}
}
最后,每个容器的都会有一个标准Value实现。比如Engine里的标准实现是StandardEngineValve,其中主要包含invoke方法的处理逻辑。在nvoke方法的最后会调用host的Pipeline的第一个Value的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) {
// 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());
}
// 调用Host的Pipeline的第一个Value处理消息
host.getPipeline().getFirst().invoke(request, response);
}
这样就实现了图上所示的消息在管道中传递的过程,由Engine到Host,再到Context,最后到Wrapper。在Wrapper中的StandardWrapperValve是tomcat处理消息的边界,在这个Value的invoke()方法中,消息被传递给了Servlet,从此进入了spring mvc的领域。但在此之前,tomcat先用Filter对消息进行了拦截处理,由于Filter是一个项目中会用到比较多的组件,和spring mvc的Interceptor有很多相似之处,所以这一部分内容单独放在在下一篇中重点讲。
相关文章:
tomcat + spring mvc原理(一):tomcat原理综述和静态架构
tomcat + spring mvc原理(二):tomcat容器初始化加载和启动
tomcat + spring mvc原理(三):tomcat网络请求的监控与处理1
tomcat + spring mvc原理(五):tomcat Filter组件实现原理