本文主要目的是讲解tomcat中的pipeline机制,涉及部分源码分析

之前我们在前面的文章介绍过,tomcat中Container有4种,分别是Engine,Host,Context,Wrapper,这4个Container的实现类分别是StandardEngine,StandardHost,StandardContext,StandardWrapper。4种容器的关系是包含关系,Engine包含Host,Host包含Context,Context包含Wrapper,Wrapper则代表最基础的一个Servlet

之前在tomcat架构简述那篇文章中介绍过,tomcat由ConnectorContainer两部分组成,而当网络请求过来的时候Connector先将请求包装为Request,然后将Request交由Container进行处理,最终返回给请求方。而Container处理的第一层就是Engine容器,但是在tomcat中Engine容器不会直接调用Host容器去处理请求,那么请求是怎么在4个容器中流转的,4个容器之间是怎么依次调用的,我们今天来讲解下。

当请求到达Engine容器的时候,Engine并非是直接调用对应的Host去处理相关的请求,而是调用了自己的一个组件去处理,这个组件就叫做pipeline组件,跟pipeline相关的还有个也是容器内部的组件,叫做valve组件。

Pipeline的作用就如其中文意思一样管道,可以把不同容器想象成一个独立的个体,那么pipeline就可以理解为不同容器之间的管道,道路,桥梁。那Valve这个组件是什么东西呢?Valve也可以直接按照字面意思去理解为阀门。pipeline是通道,valve是阀门,他们两有什么关系呢?

就像上图那样,每个管道上面都有阀门,PipelineValve关系也是一样的。Valve代表管道上的阀门,可以控制管道的流向,当然每个管道上可以有多个阀门。如果把Pipeline比作公路的话,那么Valve可以理解为公路上的收费站,车代表Pipeline中的内容,那么每个收费站都会对其中的内容做一些处理(收费,查证件等)。

好了举例说完了,我们继续回归tomcat。在Catalina中,我们有4种容器,每个容器都有自己的Pipeline组件,每个Pipeline组件上至少会设定一个Valve(阀门),这个Valve我们称之为BaseValve(基础阀)。基础阀的作用是连接当前容器的下一个容器(通常是自己的自容器),可以说基础阀是两个容器之间的桥梁。

Pipeline定义对应的接口Pipeline,标准实现了StandardPipelineValve定义对应的接口Valve,抽象实现类ValveBase,4个容器对应基础阀门分别是StandardEngineValve,StandardHostValve,StandardContextValve,StandardWrapperValve。在实际运行中Pipeline,Valve运行机制如下图。

在单个容器中Pipeline,Valve运行图

Catalina中Pipeline,Valve运行图

可以看到在同一个Pipeline上可以有多个Valve,每个Valve都可以做一些操作,无论是Pipeline还是Valve操作的都是RequestResponse。而在容器之间PipelineValve则起到了桥梁的作用,那么具体内部原理是什么,我们开始查看源码。

Valve

public interface Valve {public String getInfo();public Valve getNext();public void setNext(Valve valve);public void backgroundProcess();public void invoke(Request request, Response response) throws IOException, ServletException;public void event(Request request, Response response, CometEvent event) throws IOException,ServletException;public boolean isAsyncSupported();}

先看Valve接口的方法定义,方法不是很多,这里只介绍setNext(),getNext()。在上面我们也看到了一个Pipeline上面可以有很多Valve,这些Valve存放的方式并非统一存放在Pipeline中,而是像一个链表一个接着一个。当你获取到一个Valve实例的时候,调用getNext()方法即可获取在这个Pipeline上的下个Valve实例。

Pipeline

//pipeline 接口
public interface Pipeline {public Valve getBasic();public void setBasic(Valve valve);public void addValve(Valve valve);public Valve[] getValves();public void removeValve(Valve valve);public Valve getFirst();public boolean isAsyncSupported();public Container getContainer();public void setContainer(Container container);}

可以看出Pipeline中很多的方法都是操作Valve的,包括获取,设置,移除Valve,getFirst()返回的是Pipeline上的第一个Valve,而getBasic(),setBasic()则是获取/设置基础阀,我们都知道在Pipeline中,每个pipeline至少都有一个阀门,叫做基础阀,而getBasic(),setBasic()则是操作基础阀的。

StandardPipeline

public class StandardPipeline extends LifecycleBase implements Pipeline, Contained {private static final Log log = LogFactory.getLog(StandardPipeline.class);// ----------------------------------------------------------- Constructorspublic StandardPipeline() {this(null);
}public StandardPipeline(Container container) {super();setContainer(container);
}// ----------------------------------------------------- Instance Variablesprotected Valve basic = null;protected Container container = null;protected static final String info = "org.apache.catalina.core.StandardPipeline/1.0";protected Valve first = null;//1111111111
@Override
protected synchronized void startInternal() throws LifecycleException {// Start the Valves in our pipeline (including the basic), if anyValve current = first;if (current == null) {current = basic;}while (current != null) {if (current instanceof Lifecycle)((Lifecycle) current).start();current = current.getNext();}setState(LifecycleState.STARTING);
}// ------------------------------------------------------- Pipeline Methods
//2222222222222222222222
@Override
public void setBasic(Valve valve) {// Change components if necessaryValve oldBasic = this.basic;if (oldBasic == valve)return;// Stop the old component if necessaryif (oldBasic != null) {if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {try {((Lifecycle) oldBasic).stop();} catch (LifecycleException e) {log.error("StandardPipeline.setBasic: stop", e);}}if (oldBasic instanceof Contained) {try {((Contained) oldBasic).setContainer(null);} catch (Throwable t) {ExceptionUtils.handleThrowable(t);}}}// Start the new component if necessaryif (valve == null)return;if (valve instanceof Contained) {((Contained) valve).setContainer(this.container);}if (getState().isAvailable() && valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.setBasic: start", e);return;}}// Update the pipelineValve current = first;while (current != null) {if (current.getNext() == oldBasic) {current.setNext(valve);break;}current = current.getNext();}this.basic = valve;}//3333333333333333333
@Override
public void addValve(Valve valve) {// Validate that we can add this Valveif (valve instanceof Contained)((Contained) valve).setContainer(this.container);// Start the new component if necessaryif (getState().isAvailable()) {if (valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.addValve: start: ", e);}}}// Add this Valve to the set associated with this Pipelineif (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);
}//44444444444
@Override
public Valve[] getValves() {ArrayList<Valve> valveList = new ArrayList<Valve>();Valve current = first;if (current == null) {current = basic;}while (current != null) {valveList.add(current);current = current.getNext();}return valveList.toArray(new Valve[0]);}//5555555555555555
@Override
public void removeValve(Valve valve) {Valve current;if(first == valve) {first = first.getNext();current = null;} else {current = first;}while (current != null) {if (current.getNext() == valve) {current.setNext(valve.getNext());break;}current = current.getNext();}if (first == basic) first = null;if (valve instanceof Contained)((Contained) valve).setContainer(null);if (valve instanceof Lifecycle) {// Stop this valve if necessaryif (getState().isAvailable()) {try {((Lifecycle) valve).stop();} catch (LifecycleException e) {log.error("StandardPipeline.removeValve: stop: ", e);}}try {((Lifecycle) valve).destroy();} catch (LifecycleException e) {log.error("StandardPipeline.removeValve: destroy: ", e);}}container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}//666666666666@Overridepublic Valve getFirst() {if (first != null) {return first;}return basic;}
}

StandardPipeline标准实现类中我们看到了对Pipeline接口的实现,我们选了几个比较重要的方法做源码的解析。

方法1是startInternal()

    //1111111111
@Override
protected synchronized void startInternal() throws LifecycleException {// Start the Valves in our pipeline (including the basic), if anyValve current = first;if (current == null) {current = basic;}while (current != null) {if (current instanceof Lifecycle)((Lifecycle) current).start();current = current.getNext();}setState(LifecycleState.STARTING);
}

组件的start()方法,将first(第一个阀门)赋值给current变量,如果current为空,就将basic(也就是基础阀)赋值给current,接下来如果一个标准的遍历单向链表,调用每个对象的start()方法,最后将组件(pipeline)状态设置为STARTING(启动中)。

方法2

//2222222222222222222222
@Override
public void setBasic(Valve valve) {// Change components if necessary
//如果已经有基础阀(basic已经有值并且跟要设置的值一样)那么直接return
Valve oldBasic = this.basic;
if (oldBasic == valve)return;// Stop the old component if necessary
//旧的基础阀非空 那么调用其stop方法取消和对应container的关联。(销毁旧的基础阀)
if (oldBasic != null) {if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {try {((Lifecycle) oldBasic).stop();} catch (LifecycleException e) {log.error("StandardPipeline.setBasic: stop", e);}}if (oldBasic instanceof Contained) {try {((Contained) oldBasic).setContainer(null);} catch (Throwable t) {ExceptionUtils.handleThrowable(t);}}
}// Start the new component if necessary
//非空判断
if (valve == null)return;
//和Container进行关联
if (valve instanceof Contained) {((Contained) valve).setContainer(this.container);
}
//启动新的阀门
if (getState().isAvailable() && valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.setBasic: start", e);return;}
}
//遍历阀门链表将新的阀门取代旧的阀门
// Update the pipeline
Valve current = first;
while (current != null) {if (current.getNext() == oldBasic) {current.setNext(valve);break;}current = current.getNext();
}
//将基础阀设置为新的阀门
this.basic = valve;}

方法2是用来设置基础阀的方法,这个方法在每个容器的构造函数中调用,代码逻辑也比较简单,稍微注意的地方就是阀门链表的遍历。

方法3

//3333333333333333333
@Override
public void addValve(Valve valve) {// Validate that we can add this Valve// 验证Valve 关联Containerif (valve instanceof Contained)((Contained) valve).setContainer(this.container);// Start the new component if necessary// 验证组件状态,如果对的话 启动需要添加的Valve,调用start方法。if (getState().isAvailable()) {if (valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.addValve: start: ", e);}}}//如果 first变量为空,将valve赋值给first变量,并且设置 valve的下一个阀门为基础阀//之所以这样是因为,如果first为空说明这个容器只有一个基础阀,所以此次添加的阀门肯定是第一个非基础阀阀门// Add this Valve to the set associated with this Pipelineif (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触发添加阀门事件container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}

这方法是像容器中添加Valve,在server.xml解析的时候也会调用该方法,具体代码可以到Digester相关的文章中寻找。

方法4

//44444444444
@Override
public Valve[] getValves() {ArrayList<Valve> valveList = new ArrayList<Valve>();Valve current = first;if (current == null) {current = basic;}while (current != null) {valveList.add(current);current = current.getNext();}return valveList.toArray(new Valve[0]);}

获取所有的阀门,其实就是将阀门链表添加到一个集合内,最后转成数组返回。

方法5

//5555555555555555
@Override
public void removeValve(Valve valve) {Valve current;//如果first 是需要被移除的valve 那么将first的下一个阀门赋值给first//并且current 赋值null,否则current 赋值firstif(first == valve) {first = first.getNext();current = null;} else {current = first;}//遍历阀门链表 查找需要被移除的阀门//如果之前first是被移除的话 current = null是不会进入该循环while (current != null) {if (current.getNext() == valve) {current.setNext(valve.getNext());break;}current = current.getNext();}//如果first(此时已经指向下一个阀门)此时  == 基础阀,那么first置空//从这里可以看出来 first指的是第一个阀门,即使整个container只有一个基础阀门也不会指向基础阀。//first严格定义是 除了基础阀的第一个阀门。if (first == basic) first = null;//验证需要被移除的阀门 取消container关联if (valve instanceof Contained)((Contained) valve).setContainer(null);//调用阀门的生命周期 stop destroy 方法。if (valve instanceof Lifecycle) {// Stop this valve if necessaryif (getState().isAvailable()) {try {((Lifecycle) valve).stop();} catch (LifecycleException e) {log.error("StandardPipeline.removeValve: stop: ", e);}}try {((Lifecycle) valve).destroy();} catch (LifecycleException e) {log.error("StandardPipeline.removeValve: destroy: ", e);}}//触发container的移除valve事件。container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}

方法666666

//666666666666
@Override
public Valve getFirst() {if (first != null) {return first;}return basic;
}

在方法5中我们也看到了,first指向的是容器第一个非基础阀门的阀门,从方法6中也可以看出来,first在只有一个基础阀的时候并不会指向基础阀,因为如果指向基础阀的话就不需要判断非空然后返回基础阀了,这是个需要注意的点!

关于tomcat的pipeline机制就讲那么多,其实涉及的非常基础,最关键的pipelineinvoke()方法也没有看,主要invoke()方法和其他的点比较重复,准备还是放到请求在容器中的流转讲解!敬请期待!

转载于:https://www.cnblogs.com/coldridgeValley/p/5816414.html

Tomcat中容器的pipeline机制相关推荐

  1. Tomcat中session的管理机制

    https://www.cnblogs.com/coldridgeValley/p/6016211.html 如果sessionId保留在url中是以如下形式:http://xxx.com?a=1&a ...

  2. JAVA SPI机制及SPI机制在Tomcat中的应用

    SPI 是 JAVA 提供的一种服务提供发现接口,其实就是一种面向接口的编程,为接口去匹配具体服务实现的机制,这一点上与 IOC 的思想类似,都是把装配的控制权放到了程序之外,下面具体看看什么是 SP ...

  3. sklearn 中的 Pipeline 机制

    from sklearn.pipeline import Pipeline 管道机制在机器学习算法中得以应用的根源在于,参数集在新数据集(比如测试集)上的重复使用. 管道机制实现了对全部步骤的流式化封 ...

  4. 走进JavaWeb技术世界7:Tomcat中的设计模式

    本文首发于我的个人公众号:程序员江湖 欢迎大家关注我的微信公众号:程序员江湖 努力成为最有影响力的程序员自媒体,专注于面试,职场,个人提升三大主题. 干货满满,不容错过. 门面设计模式 门面设计模式在 ...

  5. tomcat中request对象是被创建的_常用开源框架中设计模式使用分析(全)

    一.前言 说起来设计模式,大家应该都耳熟能详,设计模式代表了软件设计的最佳实践,是经过不断总结提炼出来的代码设计经验的分类总结,这些模式或者可以简化代码,或者可以是代码逻辑开起来清晰,或者对功能扩展很 ...

  6. Tomcat - 都说Tomcat违背了双亲委派机制,到底对不对?

    文章目录 类加载的本质 JVM 双亲委派机制 BootstrapClassLoader(启动类加载器) ExtensionClassLoader AppClassLoader Tomcat的 类加载顺 ...

  7. 【Docker学习笔记 五】深入理解Docker容器数据卷机制

    前几篇Blog是对Docker的一个入门和初识,本篇Blog开始就详细学习下一个新的理论基础概念:Volume,也就是容器数据卷,听起来名字高大上,实际上就是一个宿主机的目录而已,为什么需要容器数据卷 ...

  8. Tomcat中定制阀门

    我们说管道机制给我们带来了更好的扩展性,Tomcat中在扩展性方面具体如何体现,这便是本节讨论的内容.从上节了解到基础阀门是必须执行的,假如你需要一个额外的逻辑处理阀门,可以添加一个非基础阀门. 我的 ...

  9. JVM17_Tomcat打破双亲委派机制、执行顺序、底层代码原理、Tomcat|JDBC破坏双亲委派机制带来的面试题

    文章目录 ①. Tomcat类加载机制 ②. Tomcat执行顺序 ③. ClassLoader的创建 ④. ClassLoader加载过程 ⑤. Tomcat破坏双亲委派机制带来的面试题 ①. To ...

最新文章

  1. Android语音信号波形显示
  2. Struts权威著作
  3. 在Windows下为PHP安装redis扩展
  4. linux解压文件名乱码,linux下解压zip文件时,文件名乱码的解决(转载)
  5. 轻量级ORM框架Dapper应用四:使用Dapper返回多个结果集
  6. [译]ASP.NET Core 2.0 带初始参数的中间件
  7. bootstrap-按钮的创建
  8. ubuntu下的qt程序移植至ARM开发板
  9. [JNI] 开发之旅 (2)解释jni helloworld实例
  10. pdo oracle extension php.ini,php,pdo怎么连接oracle数据库?
  11. Mac上很好用的播放器Elmedia Player
  12. 科幻小说《霜与火》 by 雷·布雷德伯里
  13. 青岛胶州职业教育中心计算机基础专业证,胶州市职业教育中心学校着眼胶州发展大局,精准培养人才...
  14. 串口调试精灵的使用和串口程序调试技巧
  15. 计算机考研英语复试专有名词翻译
  16. 北大最强扫地僧“韦东奕”爆红全网
  17. 使用superset完成mysql数据库或者hive数据库的数据可视化
  18. python翻转棋_奥赛罗棋reverse
  19. ssm+mybatis+mp
  20. c语言表达式优先级()

热门文章

  1. ARPG手游性能分析报告:加载、GC、内存需重点关注
  2. 【Auto.js】[系统Intent]_系统设置页面的相关intent跳转
  3. eclipse集成processing、PApplet、proclipsing 问题
  4. Oracle12C的卸载过程
  5. 抛开vue-cli 利用requireJS搭建一个vue项目
  6. Swift5.1 语言指南(一) 关于Swift
  7. 04-String——课后作业1:字串加密
  8. kvm cobbler无人值守批量安装操作系统
  9. [置顶] “河软CSDN2011级表彰暨实习动员大会”顺利召开!
  10. [转]轻松掌握Ajax.net系列教程十六:使用DropDownExtender