tomcat + spring mvc 原理(四):tomcat网络请求的监控与处理2

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组件实现原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值