Pipeline

节选部分源码、源码版本 Tomcat8.5

处理模式

Pipeline--Valve是一种责任链模式,它和普通责任链模式有两点区别:

  • 每个Pipeline都是有特定的Valve,而且是在管道的最后一个执行,这个Valve叫BaseValve,并且BaseValve是不可删除的;
  • 在上层容器的管道的BaseValve中会调用下层容器的管道。
  • "容器的 过滤器"

Tomcat中的管道

​ Tomcat按照包含关系有4个级别的容器,标准实现分别是:

  • StandardEngine
  • StandardHost
  • StandardContext
  • StandardWrapper

请求对象和响应对象分别在这4个容器之间通过管道机制进行传递。

初始化

  • 在 server.xml 中 配置 的默认 阀门:
<Engine name="Catalina" defaultHost="localhost"><Host name="localhost"  appBase="webapps"unpackWARs="true" autoDeploy="true"><!-- 默认 Valve --><Valve className="org.apache.catalina.valves.AccessLogValve"directory="logs"prefix="localhost_access_log"suffix=".txt"<!-- maxDays="5" -->pattern="%h %l %u %t &quot;%r&quot; %s %b" /><!-- 自定义 valve --><Valve className="org.apache.catalina.valves.WswAccessValve"/></Host>
</Engine>

自定义Valve

public class WswAccessValve extends ValveBase {@Overridepublic void invoke(Request request, Response response) {String uri = request.getRequestURI();System.out.println("uri = " + uri);getNext().invoke(request, response);}
}

四个基础Valve

public StandardEngine() {super();pipeline.setBasic(new StandardEngineValve());
}public StandardHost() {super();pipeline.setBasic(new StandardHostValve());
}public StandardContext() {super();pipeline.setBasic(new StandardContextValve());
}public StandardWrapper() {super();pipeline.setBasic(new StandardWrapperValve());
}

构造Valve链

// 在处理 server.xml 中配置的 Valve 时
// StandardPipeline
@Override
public void setBasic(Valve valve) {Valve oldBasic = this.basic;if (oldBasic == valve) { return; }// 不会走的if (oldBasic != null) {if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {((Lifecycle) oldBasic).stop();}if (oldBasic instanceof Contained) {((Contained) oldBasic).setContainer(null);}if (valve == null) { return; }if (valve instanceof Contained) {((Contained) valve).setContainer(this.container);}// 未执行if (getState().isAvailable() && valve instanceof Lifecycle) {((Lifecycle) valve).start();}Valve current = first;while (current != null) {if (current.getNext() == oldBasic) {current.setNext(valve);break;}current = current.getNext();}this.basic = valve;
}@Override
public void addValve(Valve valve) {// 验证 是否可以 绑定 容器(Engine、Host等)if (valve instanceof Contained)((Contained) valve).setContainer(this.container);// 是否有必要调用 start 方法【默认没有调用,不属于LifeCycle】if (getState().isAvailable()) {if (valve instanceof Lifecycle) {((Lifecycle) valve).start();}}// first valve == null,就设置为第一个if (first == null) {first = valve;valve.setNext(basic);} else {Valve current = first;while (current != null) {if (current.getNext() == basic) {// 按照在xml中定义的顺序+basicValve// 组装成链式结构,first + 新增 valve + basicValvecurrent.setNext(valve);valve.setNext(basic);break;}current = current.getNext();}}// [没有执行]container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}

执行流程

  • 在收到请求后,调用"容器过滤器"
// org.apache.catalina.connector.CoyoteAdapter
@Override
public void service(org.apache.coyote.Request req,                                                  org.apache.coyote.Response res) {// 此处调用 Pipeline Value 的 invoke 方法。(Engine是最顶层容器)connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);}
}
// StandardPipeline
@Override
public Valve getFirst() {// 如果有注册Valve,就返回if (first != null) {return first;}// 返回注册的 BasicValvereturn basic;
}
  • StandardEngineValve
@Override
public final void invoke(Request request, Response response) {// 根据 当前 request 找到合适的 host,通过 MappingData Host host = request.getHost();// 没有找到 host,不走了if (host == null) {response.sendError(HttpServletResponse.SC_BAD_REQUEST,sm.getString("standardEngine.noHost"));return;}// 执行下一个 Valvehost.getPipeline().getFirst().invoke(request, response);
}
  • StandardHostValve
@Override
public final void invoke(Request request, Response response) {Context context = request.getContext();// 未找到匹配的 项目if (context == null) {response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,sm.getString("standardHost.noContext"));return;}// debug 后 都为 falseboolean asyncAtStart = request.isAsync();boolean asyncDispatching = request.isAsyncDispatching();try {context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);// 见下面的说明if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {return;}try {if (!asyncAtStart || asyncDispatching) {context.getPipeline().getFirst().invoke(request, response);} else {if (!response.isErrorReportRequired()) {throw new IllegalStateException("standardHost.asyncStateError");}}} catch (Throwable t) {if (!response.isErrorReportRequired()) {request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);throwable(request, response, t);}}response.setSuspended(false);// 是否有错误信息Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);// 判断 上下文状态是否可用if (!context.getState().isAvailable()) {return;}// 查找(如果找到则呈现)应用程序级别错误页面if (response.isErrorReportRequired()) {if (t != null) {throwable(request, response, t);} else {status(request, response);}}// 调用销毁 request 的方法,见说明if (!request.isAsync() && !asyncAtStart) {context.fireRequestDestroyEvent(request.getRequest());}} finally {if (ACCESS_SESSION) {request.getSession(false);}context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);}
}
  • 在 Listener 章节中 提到 :ServletRequestListener 的 requestInitialized 方法判断
  • Line18:context.fireRequestInitEvent(request.getRequest())
// 说明
@Override
public boolean fireRequestInitEvent(ServletRequest request) {Object instances[] = getApplicationEventListeners();if ((instances != null) && (instances.length > 0)) {ServletRequestEvent event =new ServletRequestEvent(getServletContext(), request);for (int i = 0; i < instances.length; i++) {if (instances[i] == null) { continue; }if (!(instances[i] instanceof ServletRequestListener))continue;ServletRequestListener listener =(ServletRequestListener) instances[i];try {listener.requestInitialized(event);} catch (Throwable t) {request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);return false;}}}return true;
}
// 获取所有的 applicationEventListener
// 判断是否是 ServletRequestListener 类型
// 调用 listener.requestInitialized(event); 完成初始化
  • Line51:context.fireRequestDestroyEvent(request.getRequest());
@Override
public boolean fireRequestDestroyEvent(ServletRequest request) {Object instances[] = getApplicationEventListeners();if ((instances != null) && (instances.length > 0)) {ServletRequestEvent event =new ServletRequestEvent(getServletContext(), request);for (int i = 0; i < instances.length; i++) {int j = (instances.length -1) -i;if (instances[j] == null) { continue; }if (!(instances[j] instanceof ServletRequestListener))continue;ServletRequestListener listener =(ServletRequestListener) instances[j];try {listener.requestDestroyed(event);} catch (Throwable t) {request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);return false;}}}return true;
}
// 和初始化方法极其相似
  • StandardContextValve
@Override
public final void invoke(Request request, Response response) {// 测试的请求路径【/index.html】MessageBytes requestPathMB = request.getRequestPathMB();// 不允许直接访问 WEB-INF or META-INF,在这里判断的if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))|| (requestPathMB.equalsIgnoreCase("/META-INF"))|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {response.sendError(HttpServletResponse.SC_NOT_FOUND);return;}// 根据当前 request 选择合适的 wrapper【servlet】Wrapper wrapper = request.getWrapper();// 判断状态if (wrapper == null || wrapper.isUnavailable()) {response.sendError(HttpServletResponse.SC_NOT_FOUND);return;}// 发送确认请求,HTTP/1.1 100try {response.sendAcknowledgement();} catch (IOException ioe) {return;}// 判断是否支持异步if (request.isAsyncSupported()) {request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());}// 调用 Wrapper 的 pipeline 处理wrapper.getPipeline().getFirst().invoke(request, response);
}
  • Line6:重点

  • StandardWrapperValve

@Override
public final void invoke(Request request, Response response)throws IOException, ServletException {boolean unavailable = false;requestCount.incrementAndGet();StandardWrapper wrapper = (StandardWrapper) getContainer();Servlet servlet = null;Context context = (Context) wrapper.getParent();// Check for the application being marked unavailableif (!context.getState().isAvailable()) {response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardContext.isUnavailable"));unavailable = true;}// 分配 servlet 实例处理此次请求if (!unavailable) {// 会判断  Servlet 是否有  // ******* SingleThreadModel 接口 ******************servlet = wrapper.allocate();}// 创建 FilterApplicationFilterChain filterChain =ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);// Call the filter chain for this requestif ((servlet != null) && (filterChain != null)) {if (request.isAsyncDispatching()) {request.getAsyncContextInternal().doInternalDispatch();} else {// 此处 调用 过滤器方法filterChain.doFilter(request.getRequest(),response.getResponse());}} else {if (request.isAsyncDispatching()) {request.getAsyncContextInternal().doInternalDispatch();} else {filterChain.doFilter(request.getRequest(), response.getResponse());}}// 释放 filterChainif (filterChain != null) {filterChain.release();}// 释放 instanceif (servlet != null) {wrapper.deallocate(servlet);}// 如果不可用,卸载 & 释放 instanceif ((servlet != null) &&(wrapper.getAvailable() == Long.MAX_VALUE)) {wrapper.unload();}
}
  • Line25:关联 Filter 章节

转载于:https://www.cnblogs.com/wansw/p/10755175.html

Tomcat中Pipeline相关推荐

  1. Tomcat中容器的pipeline机制

    本文主要目的是讲解tomcat中的pipeline机制,涉及部分源码分析 之前我们在前面的文章介绍过,tomcat中Container有4种,分别是Engine,Host,Context,Wrappe ...

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

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

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

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

  4. 使用 CAS 在 Tomcat 中实现单点登录

    CAS 介绍 CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目.CAS 具有以下特 ...

  5. 简单介绍Tomcat中catalina.out 和 catalina.log的区别和用途

    本文主要介绍了Tomcat中catalina.out 和 catalina.log的区别和用途详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 catalina. ...

  6. tomcat中conf/server.xml配置线程池不生效的问题注意添加线程池节点executor后要在connector中增加executor属性

    优化连接数,主要是在conf/server.xml配置文件中进行修改. 优化线程数 找到Connector port="8080" protocol="HTTP/1.1& ...

  7. 在Tomcat中部署Java Web应用程序几种方式

    在Tomcat中部署Java Web应用程序有两种方式:静态部署和动态部署.在下文中$CATALINA_HOME指的是Tomcat根目录.  一.静态部署       静态部署指的是我们在服务器启动之 ...

  8. 项目部署不到tomcat中的原因和解决方法

    部署项目到tomcat中出现一个"黄叹号!".报错如下: (1) Deployment is out of date due to changes in the underlyin ...

  9. Tomcat中的Session小结

    什么是Session 对Tomcat而言,Session是一块在服务器开辟的内存空间,其存储结构为ConcurrentHashMap: Session的目的 Http协议是一种无状态协议,即每次服务端 ...

  10. Spring Cloud 注册中心在tomcat中部署

    前言 最近刚刚接触spring boot 和spring cloud,只知道可以直接通过main方法启动服务,一直不会将项目部署到tomcat中,今天学了一下,做个记录备忘. 步骤 pom文件 在po ...

最新文章

  1. matplotlib可视化基本散点图、在图像多个指定区域绘制方框、进行自定义色彩填充(Draw Multiple Rectangles)
  2. 四、物理数据模型PDM(Physical Data Model )
  3. 谷歌和 Facebook 是如何给工程师定职级和薪水的?
  4. java异常什么时候抛出异常,java - 什么时候应该抛出IllegalArgumentException?
  5. Flutter的文本控件的基本使用
  6. 我是如何零基础入门前端开发的(2021 版)
  7. Valhalla LW2的进展–内联类型
  8. Domino的压缩数据库的Load Compact命令
  9. 深入理解java虚拟机 (二) 第二版
  10. Zabbix通过自动发现监控端口
  11. MYSQL的两种存储引擎区别
  12. 一个小型的中文文本分类系统(项目链接文末)——《ML算法原理和实践》学习笔记
  13. Unity实战篇 | 游戏中控制 地图无限自动化生成 的方法,进一步优化项目
  14. App 抓包工具一(Charles)
  15. EndNote插入word不出现上角标,插入文献前不出现数字解决方案
  16. Linux系统时钟同步
  17. word中使用通配符替换【持续更新系列】
  18. 华硕 PRIME Z490-PLUS+i7-10700K黑苹果EFI引导文件
  19. 第二本书unit5 lvm管理
  20. Linux---Docker镜像使用cx_Oracle连接Oracle数据库

热门文章

  1. shell按照时间排序_初识Shell(3)
  2. PingInfoView,中文,以及ping包+描述的使用。
  3. 帆软统计函数之统计有效性
  4. c语言经典程序100例50行以上,C语言非常简单的字符统计程序50行
  5. react前端封装接口弹出错误_react+ts打包发布后报Minified React error ..这种错误
  6. 关于 Ubuntu Server 18.04 的网络(dchp/dns/route/PPPoE)
  7. Android成企业安全主要威胁的十大原因
  8. python各个模块
  9. SQL正则表达式的妙用
  10. 数据--第22课 - 队列的定义及实现