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 {

    @Override
public 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 + basicValve
current.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;
}
// 返回注册的 BasicValve
return 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;
}
// 执行下一个 Valve
host.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 后 都为 false
boolean 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 100
try {
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 unavailable
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}
// 分配 servlet 实例处理此次请求
if (!unavailable) {
// 会判断 Servlet 是否有
// ******* SingleThreadModel 接口 ******************
servlet = wrapper.allocate();
} // 创建 Filter
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request
if ((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());
}
} // 释放 filterChain
if (filterChain != null) {
filterChain.release();
} // 释放 instance
if (servlet != null) {
wrapper.deallocate(servlet);
} // 如果不可用,卸载 & 释放 instance
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
}
  • 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. golang 编码转化

    在网上搜索golang编码转化时,我们经常看到的文章是使用下面一些第三方库: https://github.com/djimenez/iconv-go https://github.com/qiniu ...

  2. hdu 1397 (素数判定)

    一开始提交了这个,果断TLE #include <cstdio> #include <iostream> #include <string> #include &l ...

  3. Hibernate入门(四)—— 查询

    一.Hibernate查询 1.Hibernate检索(查询)方式的分类 OID检索 :根据主键查询,get/load 对象导航检索 :通过一个对象获得其关联对象.[重点] Category cate ...

  4. 【PyQt5 学习记录】004:简单QThread笔记

    在文本编辑框中每隔几秒添加一行文本,代码如下: #!/usr/bin/python3 # -*- coding:utf-8 -*- import sys from PyQt5.QtWidgets im ...

  5. ionic--配置路由

    1.ng-route index中引用文件: <script src="ionic.bundle.js"></script> <script src= ...

  6. .net reflector+reflexil修改编译后的dll文件

    1.用reflector打开相关的dll文件. 2.如果reflector中没有reflexil插件,点击工具栏中的Tools->Add-Ins 3.找到需要修改的文件,双击打开该文件:点击To ...

  7. Linux 安装命令

  8. Install guide for OpenLDAP and GOsa 2 on Ubuntu & Debian

    First we will install OpenLDAP by running the command as root: apt-get install slapd ldap-utils ldap ...

  9. Wireframe Process

  10. MySQL两个表联合查询并按时间排序

    有一张资金记录表,一张金币记录表,想以时间为单位,降序合并排列他们之间的信息,查询SQL如下: select * from (select * from t_money_logs union sele ...