从servlet到springboot(16) Tomcat源码分析(3).责任链模式与Pipeline-Value管道

56 篇文章 2 订阅

之前我们已经把tomcat的源码分析完了,但是还有一个内容没有说,那就是Pipeline-Value管道,Tomcat中Container处理请求是使用Pipeline-Value管道模式来处理的。实际上这是责任链模式的一个变种,所以我们要优先说明下责任链模式

责任链模式

先讲一个故事:

古代女子通常是比较没地位的,如果一个妇女要出门,首先必须经过他父亲的同意,如果父亲没有回应就交给丈夫,如果丈夫也没回应则会交给儿子,也就是下面这张图的模式

这就是一个经典的责任链模式,我们用代码来实现

先定义一个处理类

public abstract  class Handler {
    public final static int FATHER_LEVEL_REQUEST = 1;

    public final static int HUSBAND_LEVEL_REQUEST = 2;

    public final static int SON_LEVEL_REQUEST = 3;

    private int level = 0;

    private Handler nextHandler;

    public Handler(int _level){
        this.level = _level;
    }

    public final void HandlerMessage(IWomen iWomen){
        if (iWomen.getType() == level){
            this.response(iWomen);
        }else{
            if (this.nextHandler != null ){
                this.nextHandler.HandlerMessage(iWomen);
            }else{
                System.out.println("无人处理");
            }
        }
    }

    protected abstract void response(IWomen iWomen);

    public void setNextHandler(Handler _handler){
        this.nextHandler = _handler;
    }

}

然后定义father/husband和son

public class Father extends Handler {
    public Father() {
        super(Handler.FATHER_LEVEL_REQUEST);
    }

    @Override
    protected void response(IWomen iWomen) {
        System.out.println("------------女儿向父亲指示----------");
        System.out.println(iWomen.getRequest());
        System.out.println("父亲的答案是:同意");
    }
}
public class Husband extends Handler{
    public Husband() {
        super(Handler.HUSBAND_LEVEL_REQUEST);
    }

    @Override
    protected void response(IWomen iWomen) {
        System.out.println("------------妻子向丈夫指示----------");
        System.out.println(iWomen.getRequest());
        System.out.println("丈夫的答案是:同意");
    }
}
public class Son extends Handler{
    public Son() {
        super(Handler.SON_LEVEL_REQUEST);
    }

    @Override
    protected void response(IWomen iWomen) {
        System.out.println("------------母亲向儿子指示----------");
        System.out.println(iWomen.getRequest());
        System.out.println("儿子的答案是:同意");
    }
}

 

定义妇女接口

public interface IWomen {
    int getType();

    String getRequest();
}

实现类

public class Women implements IWomen{

    private int type = 0;

    private String request = "";

    public Women(int _type,String _request){
        this.type = _type;
        switch (this.type){
            case 1: this.request = "女儿的请求是:" + _request;
            break;
            case 2: this.request = "妻子的请求是:"+ _request;
            break;
            case 3 : this.request = "母亲的请求是" + _request;
        }
    }
    @Override
    public int getType() {
        return this.type;
    }

    @Override
    public String getRequest() {
        return this.request;
    }
}

场景

public class Client {
    public static void main(String[] args) {
        Random rand = new Random();
        List<IWomen> lists = new ArrayList<>();
        for (int i =0;i<5;i++){
            lists.add(new Women(rand.nextInt(4),"我要去逛街"));
        }
        Handler father = new Father();
        Handler husband = new Husband();
        Handler son = new Son();
        father.setNextHandler(husband);
        husband.setNextHandler(son);
        for (IWomen w :lists){
            father.HandlerMessage(w);
        }
    }
}

 

以上就是责任链模式,责任链模式在框架中有大量应用,比如mybatis的Plugins,dubbo中的filter都运用了责任链模式

---------------------------------------------------------------------------------------------------------------------------------------------------

上面用一个简单的例子来说明了责任链模式,接下去我们来分析tomcat中的Pipeline-Value模式

tomcat中每个容器都有自己的Pipeline,比如Engine有EnginePipeline,每个pipeline都有特定的Value,而且都是在管道的最后一个执行,这个Value叫BaseValue,比如EnginePipeline的就叫StandardEngineValve。我们可以用下图来表示

toamcat中pipeline在ContainerBase中定义,在ContianerBase的startInternal方法中启动

pipeline中请求实现方法如下图

 

Connector–>Engine的Pipeline的ValveA中–>Engine Valve–>Host Pipeline的Error Report Valve和Host Value–>Context Valve–>Wrapper Valve中,在这里会经过一个过滤器链(Filter Chain)–>Servlet中。 
Servlet处理完成后一步步返回,最后Connector拿到response。


   一个pipeline包含多个Valve,这些阀共分为两类,一类叫基础阀(通过getBasic、setBasic方法调用),一类是普通阀(通过addValve、removeValve调用)。 
一个管道一般有一个基础阀(通过setBasic添加),可以有0到多个普通阀(通过addValve添加)。
 

Pipeline嗲用所包含的Value的invoke方法来处理请求。最终会到达StandardWrapperValve

我们依次说明每个基础value

1 .StandardEngineValue 
此阀门在调用时会获取请求主机的host对象,同时负责调用host对象管道中的第一个阀门。

public final void invoke(Request request,Response response) throws IOException, ServletException{
    Host host = request.getHost();
    host.getPipeline().getFirst().incoke(request,response);
}

2.StandardHostValue: 
获取请求对应的上下文的context对象并调用context对象中管道的第一个阀门。

//触发request初始化事件 
Context context = request.getContext(); 
//更新上次会话的访问时间 
context.getPipeline().getFirst().invoke(request,response); 
}

3.StandardContextValue 
获取请求对应的wrapper对象,在向着客户端发送通知报文“HTTP/1/1 100 Continue”,最后调用wrapper对象中管道的第一个阀门。

public  final  void invoke(Request request,Response response) throws IOException,ServletException{
    //判断访问路径是否包含WEB-INF或者META-INF,禁止访问此目录
    Wrapper wrapper = request.getWrapper();
    //向客户端发送"HTTP/1.1 100 Continue" 通知
    wrapper.getPipeline().getFirst().invoke(request,response);
}
4.StandardWrapperValue, 
请求在管道中最后到达StandardWrapperValve, 源码如下:

public final void invoke(Request request, Response response)  
    throws IOException, ServletException {  

    ......  
    requestCount++;  
    //定位wrapper  
    StandardWrapper wrapper = (StandardWrapper) getContainer();  
    Servlet servlet = ;  
    Context context = (Context) wrapper.getParent();  

    ......  

    // Allocate a servlet instance to process this request  
    try {  
        if (!unavailable) {  
            //加载servlet  
            servlet = wrapper.allocate();                  
        }  
    } catch (UnavailableException e) {  
        ......  
    }   
    ......  
    // 根据配置建立一个filter-servlet的处理链表,servlet在链表的尾端  
    ApplicationFilterFactory factory =  
        ApplicationFilterFactory.getInstance();  
    ApplicationFilterChain filterChain =  
        factory.createFilterChain(request, wrapper, servlet);  
    // Reset comet flag value after creating the filter chain  
    request.setComet(false);  

    // Call the filter chain for this request  
    // NOTE: This also calls the servlet's service() method  
    try {  
        String jspFile = wrapper.getJspFile();  
        if (jspFile != )  
            request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);  
        else  
            request.removeAttribute(Globals.JSP_FILE_ATTR);  
        if ((servlet != ) && (filterChain != )) {  
            // Swallow output if needed  
            if (context.getSwallowOutput()) {  
                try {  
                    SystemLogHandler.startCapture();  
                    if (comet) {  
                        filterChain.doFilterEvent(request.getEvent());  
                        request.setComet(true);  
                    } else {  
                        //调用filter-servlet链表  
                        filterChain.doFilter(request.getRequest(),   
                                response.getResponse());  
                    }  
                } finally {  
                    String log = SystemLogHandler.stopCapture();  
                    if (log !=  && log.length() > 0) {  
                        context.getLogger().info(log);  
                    }  
                }  
            } else {  
                if (comet) {  
                    request.setComet(true);  
                    filterChain.doFilterEvent(request.getEvent());  
                } else {  
                    //调用filter-servlet链表  
                    filterChain.doFilter  
                        (request.getRequest(), response.getResponse());  
                }  
            }  

        }  
        request.removeAttribute(Globals.JSP_FILE_ATTR);  
    } catch (ClientAbortException e) {  
        request.removeAttribute(Globals.JSP_FILE_ATTR);  
        throwable = e;  
        exception(request, response, e);  
    }   
    ......  
}  

从StandardEngineValve开始, 所有的基础阀的实现最后都会调用其下一级容器,所有的普通阀都会执行getNext().invoke(request, response);,一直到StandardWrapperValve,完成请求处理过程。

因为Wrapper是对一个Servlet的包装,所以它的基础阀内部调用的过滤器链的doFilter方法和Servlet的service方法。 
上述机制保证了请求传递到servlet去处理。
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值