Pipeline

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

处理模式

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

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

Tomcat中的管道

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

  • StandardEngine
  • StandardHost
  • StandardContext
  • StandardWrapper

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

初始化

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

自定义Valve

  1. public class WswAccessValve extends ValveBase {
  2. @Override
  3. public void invoke(Request request, Response response) {
  4. String uri = request.getRequestURI();
  5. System.out.println("uri = " + uri);
  6. getNext().invoke(request, response);
  7. }
  8. }

四个基础Valve

  1. public StandardEngine() {
  2. super();
  3. pipeline.setBasic(new StandardEngineValve());
  4. }
  5. public StandardHost() {
  6. super();
  7. pipeline.setBasic(new StandardHostValve());
  8. }
  9. public StandardContext() {
  10. super();
  11. pipeline.setBasic(new StandardContextValve());
  12. }
  13. public StandardWrapper() {
  14. super();
  15. pipeline.setBasic(new StandardWrapperValve());
  16. }

构造Valve链

  1. // 在处理 server.xml 中配置的 Valve 时
  2. // StandardPipeline
  3. @Override
  4. public void setBasic(Valve valve) {
  5. Valve oldBasic = this.basic;
  6. if (oldBasic == valve) { return; }
  7. // 不会走的
  8. if (oldBasic != null) {
  9. if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
  10. ((Lifecycle) oldBasic).stop();
  11. }
  12. if (oldBasic instanceof Contained) {
  13. ((Contained) oldBasic).setContainer(null);
  14. }
  15. if (valve == null) { return; }
  16. if (valve instanceof Contained) {
  17. ((Contained) valve).setContainer(this.container);
  18. }
  19. // 未执行
  20. if (getState().isAvailable() && valve instanceof Lifecycle) {
  21. ((Lifecycle) valve).start();
  22. }
  23. Valve current = first;
  24. while (current != null) {
  25. if (current.getNext() == oldBasic) {
  26. current.setNext(valve);
  27. break;
  28. }
  29. current = current.getNext();
  30. }
  31. this.basic = valve;
  32. }
  33. @Override
  34. public void addValve(Valve valve) {
  35. // 验证 是否可以 绑定 容器(Engine、Host等)
  36. if (valve instanceof Contained)
  37. ((Contained) valve).setContainer(this.container);
  38. // 是否有必要调用 start 方法【默认没有调用,不属于LifeCycle】
  39. if (getState().isAvailable()) {
  40. if (valve instanceof Lifecycle) {
  41. ((Lifecycle) valve).start();
  42. }
  43. }
  44. // first valve == null,就设置为第一个
  45. if (first == null) {
  46. first = valve;
  47. valve.setNext(basic);
  48. } else {
  49. Valve current = first;
  50. while (current != null) {
  51. if (current.getNext() == basic) {
  52. // 按照在xml中定义的顺序+basicValve
  53. // 组装成链式结构,first + 新增 valve + basicValve
  54. current.setNext(valve);
  55. valve.setNext(basic);
  56. break;
  57. }
  58. current = current.getNext();
  59. }
  60. }
  61. // [没有执行]
  62. container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
  63. }

执行流程

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

  • StandardWrapperValve

  1. @Override
  2. public final void invoke(Request request, Response response)
  3. throws IOException, ServletException {
  4. boolean unavailable = false;
  5. requestCount.incrementAndGet();
  6. StandardWrapper wrapper = (StandardWrapper) getContainer();
  7. Servlet servlet = null;
  8. Context context = (Context) wrapper.getParent();
  9. // Check for the application being marked unavailable
  10. if (!context.getState().isAvailable()) {
  11. response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
  12. sm.getString("standardContext.isUnavailable"));
  13. unavailable = true;
  14. }
  15. // 分配 servlet 实例处理此次请求
  16. if (!unavailable) {
  17. // 会判断 Servlet 是否有
  18. // ******* SingleThreadModel 接口 ******************
  19. servlet = wrapper.allocate();
  20. }
  21. // 创建 Filter
  22. ApplicationFilterChain filterChain =
  23. ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
  24. // Call the filter chain for this request
  25. if ((servlet != null) && (filterChain != null)) {
  26. if (request.isAsyncDispatching()) {
  27. request.getAsyncContextInternal().doInternalDispatch();
  28. } else {
  29. // 此处 调用 过滤器方法
  30. filterChain.doFilter(request.getRequest(),
  31. response.getResponse());
  32. }
  33. } else {
  34. if (request.isAsyncDispatching()) {
  35. request.getAsyncContextInternal().doInternalDispatch();
  36. } else {
  37. filterChain.doFilter
  38. (request.getRequest(), response.getResponse());
  39. }
  40. }
  41. // 释放 filterChain
  42. if (filterChain != null) {
  43. filterChain.release();
  44. }
  45. // 释放 instance
  46. if (servlet != null) {
  47. wrapper.deallocate(servlet);
  48. }
  49. // 如果不可用,卸载 & 释放 instance
  50. if ((servlet != null) &&
  51. (wrapper.getAvailable() == Long.MAX_VALUE)) {
  52. wrapper.unload();
  53. }
  54. }
  • Line25:关联 Filter 章节

Tomcat中Pipeline的更多相关文章

  1. Tomcat中常见线程说明

    http://blog.csdn.NET/jeff_fangji/article/details/41786205 本文讲述了Tomcat的常见线程的功能.名称.线程池和配置等信息,其中源码来自于To ...

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

    . 门面设计模式 门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中.Standard Wrapper 到 ServletConfig 封装中.Appli ...

  3. Tomcat中的Host和Engine级别的servlet容器

    这边文章主要介绍的是Host容器 和 Engine容器.如果你想在同一个Tomcat上部署运行多个Context容器的话,你就需要使用Host容器,从理论上来讲,如果你的Tomcat只想要部署一个Co ...

  4. Tomcat中的Session小结

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

  5. Red5 1.0.0RC1 集成到tomcat6.0.35中运行&部署新的red5项目到tomcat中

    1.下载red5-war-1.0-RC1.zip 解压之得到 ROOT.war 文件. 2.处理tomcat. 下载apache-tomcat-6.0.35-windows-x86.zip包,解压到你 ...

  6. Nexus安装及部署(含如何在Tomcat中部署)

    1. Nexus价值 1)方便-节约带宽-快 2)便于统一管理 3)持续集成需要 2.Nexus下载 http://www.sonatype.org/nexus/go 3.Nexus启动 解压后进入\ ...

  7. 多MAVEN项目部署到tomcat中_之使用DBUG进行单步调试

    1.改成你想localhost:8080默认进入的目录 这个步骤会造成 code\.settings\org.eclipse.wst.common.component 文件的更改<propert ...

  8. 在tomcat中配置jdk的不同版本

    在tomcat中配置jdk的不同版本---------------------------------------------------------------------------------- ...

  9. e.Tomcat中的sendfile支持

    sendfile实质是linux系统中一项优化技术,用以发送文件和网络通信时,减少用户态空间与磁盘倒换数据,而直接在内核级做数据拷贝,这项技术是linux2.4之后就有的,现在已经很普遍的用在了C的网 ...

随机推荐

  1. tomcat-虚拟目录的映射

    虚拟目录的映射 1.新建 ${tomcat安装目录}\conf\Catalina\localhost\xxx.xml 文件. 文件内容: <Context  path="/xxx&qu ...

  2. 微服务架构之spring cloud feign

    在spring cloud ribbon中我们用RestTemplate实现了服务调用,可以看到我们还是需要配置服务名称,调用的方法 等等,其实spring cloud提供了更优雅的服务调用方式,就是 ...

  3. 下载 github 项目文件到本地方法

    下载 github 项目文件到本地方法 本篇终极,收集 3 种方法 最厉害 666 的方法 直接访问网站: 操作如下: 本地工具版下载方法 首先需要下载 git 客户端 我就不转载了,上面有客户端的使 ...

  4. linux下close 掉socket 之后 阻塞的recv 不会立即返回

    转载自:http://www.cnblogs.com/wainiwann/p/3942203.html 在开发的一个基于rtmp聊天的程序时发现了一个很奇怪的现象. 在windows下当我们执行 cl ...

  5. SpringMVC与shiro集成及配置文件说明!

    在项目中xml文件的配置是必不可少的,特别是SpringMVC框架.但是几乎所有项目的配置都是大同小异,很多人都是直接复制黏贴了事,不少人对其具体含义及用途都不甚全知.本片文章将正对项目中常用的框架S ...

  6. SQL server 和 Oracle 中列转行的小操作

    Oracle: create table zjhis.mz_zdxx_zl as select a.sfsb, wm_concat(a.zdmc) as 诊断 from zjhis.mz_zdxx a ...

  7. [翻译] PPDragDropBadgeView

    PPDragDropBadgeView https://github.com/smallmuou/PPDragDropBadgeView PPDragDropBadgeView is a badge ...

  8. SpringMvc学习---基础知识考核

    SpringMVC 1.SpringMVC的工作流程 流程 : 1.用户发送请求至前端控制器DispatcherServlet2.DispatcherServlet收到请求调用HandlerMappi ...

  9. Java实例---flappy-bird实例[最终版]

    代码分析 解析版: Java实例---flappy-bird实例解析 完整版: TestBirdFly.java package testfly; import java.awt.Color; imp ...

  10. .htaccess详解及.htaccess参数说明

    .htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录.作为用户,所能使用的命令受到 ...