1.Container的有四个子容器,分别是Engine,Host,Context,Wrapper,如下:

1、Engine:整个Catalina servlet引擎,标准实现为StandardEngine。
2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。
3、Context:表示一个web应用程序,对应着平时开发对应的一套程序,或者一个WEB-INF目录以及下面的web.xml文件一个Context可以有多个Wrapper,标准实现为StandardContext。
4、Wrapper:包装一个独立的Servlet容器,每个Wrapper封装一个Servlet,标准实现为StandardWrapper。

2.Container一个也有四个子接口Engine,Host,Context,Wrapper和一个默认实现类ContainerBase,另外这四个子容器都是对应一个StandardXXX实现类,都继承ContainerBase,并且Container还继承LifeCycle接口,所以这四个容器也是符合Tomcat的生命周期模式,结构图如下:

每个Container容器都有对应的阀Valve,多个Valve组成了Pipeline,这就是Container的具体实现过程,也可以在server.xml文件中配置Pipeline和Valve的集合实现。管道Pipe包含了容器中要执行的任务,而每一个阀Valve表示一个具体的任务,在每个管道中,都会有一个默认的阀,可以添加任意数量的阀,可通过server.xml文件配置。对过滤器熟悉的话就会发现,管道和阀的工作机制和过滤器工作机制相似,Pipeline相当于过滤器链FilterChain,Valve相当于每一个过滤器Filter。阀可以处理传给它的request对象和response对象,处理完一个Valve后接着处理下一个Valve,最后处理的阀是基础阀。下面就追踪每一个容器的管道,解析容器处理请求的流程。

首先是Engine容器,默认实现是StandardEngine,创建StandardEngine时实例化其基础阀,代码如下:

public StandardEngine() {

    super();
//设置基础阀StandardEngineValve
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10; }

继续跟踪StandardEngineValve的invoke()方法,源码为:

public final void invoke(Request request, Response response)
throws IOException, ServletException { // Select the Host to be used for this Request
// 选出和该request相关的Host,在MappingData中保存了请求和容器(Host,Context,Wrapper)之间的映射
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
} // Ask this Host to process this request
// host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理
host.getPipeline().getFirst().invoke(request, response); }

StandardEngineValve的invoke()方法是在CoyoteAdapter类中service方法中调用的,也就是Connector将请求交给Container的过程:

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception { Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES); ...... // Parse and set Catalina and configuration specific
// request parameters
//根据request得到对应的MappingData,里面保存了解析地址对应的Engine,Host,Context,Wrapper,在后面的管道中使用
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
//得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
} ......
}

这个service方法就是SocketProcessor#doRun--->Http11Processor#service--->CoyoteAdapter#service

同样Host容器构造器中设置了其基础阀StandardHostValve:

public StandardHost() {

    super();
pipeline.setBasic(new StandardHostValve()); }

同样跟踪StandardHostValve的invoke方法:

public final void invoke(Request request, Response response)
throws IOException, ServletException { // Select the Context to be used for this Request
// 该request容器关联的Context,保存在MappingData中
Context context = request.getContext();
if (context == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
} //是否支持异步
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
} boolean asyncAtStart = request.isAsync();
boolean asyncDispatching = request.isAsyncDispatching(); try {
//设置StandardHostValve的类加载器
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
// Don't fire listeners during async processing (the listener
// fired for the request that called startAsync()).
// If a request init listener throws an exception, the request
// is aborted.
return;
} // Ask this Context to process this request. Requests that are in
// async mode and are not being dispatched to this resource must be
// in error and have been routed here to check for application
// defined error pages.
try {
// 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中
if (!asyncAtStart || asyncDispatching) {
context.getPipeline().getFirst().invoke(request, response);
} else {
// Make sure this request/response is here because an error
// report is required.
if (!response.isErrorReportRequired()) {
throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
// If a new error occurred while trying to report a previous
// error allow the original error to be reported.
if (!response.isErrorReportRequired()) {
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
throwable(request, response, t);
}
} // Now that the request/response pair is back under container
// control lift the suspension so that the error handling can
// complete and/or the container can flush any remaining data
response.setSuspended(false); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); // Protect against NPEs if the context was destroyed during a
// long running request.
if (!context.getState().isAvailable()) {
return;
} // Look for (and render if found) an application level error page
//设置错误页面
if (response.isErrorReportRequired()) {
if (t != null) {
throwable(request, response, t);
} else {
status(request, response);
}
} if (!request.isAsync() && !asyncAtStart) {
context.fireRequestDestroyEvent(request.getRequest());
}
} finally {
// Access a session (if present) to update last accessed time, based
// on a strict interpretation of the specification
if (ACCESS_SESSION) {
request.getSession(false);
} context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
}
}

Context和Wrapper的管道和阀的实现过程与Engine和Host完全一样,不再继续分析。最后主要解析StandardWrapperValve的invoke()方法,看该方法如何将request交个一个servlet处理。鉴于该方法源码太长,只展示出了部分重要代码。

public final void invoke(Request request, Response response)
throws IOException, ServletException { ......
//获取关联的StandardWrapper
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
//wrapper的父容器Context
Context context = (Context) wrapper.getParent(); ...... // Allocate a servlet instance to process this request
// 分配一个servlet实例处理该request
try {
if (!unavailable) {
//servlet可用时,分配servlet,接下来会跟踪allocate()方法
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
//分别设置了503错误和404 not found
......
} catch (ServletException e) {
......
} catch (Throwable e) {
......
} ......
// Create the filter chain for this request
// 为该request设置过滤器
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
// 过滤器作用于该request,并且此过程中调用了servlet的service()方法
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
} }
} catch (ClientAbortException e) {
......
} catch (IOException e) {
......
} catch (UnavailableException e) {
......
} catch (ServletException e) {
......
} catch (Throwable e) {
......
} // Release the filter chain (if any) for this request
// 释放该request的过滤链
if (filterChain != null) {
filterChain.release();
} // Deallocate the allocated servlet instance
try {
// 回收servlet容器实例
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
......
} ......
}

接着跟踪Wrapper的allocate源码:该方法主要功能是分配一个初始化了的servlet实例,其service方法可以被调用。

public Servlet allocate() throws ServletException {

    // If we are currently unloading this servlet, throw an exception
// servlet类没有加载时剖出异常
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
} boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
// servlet没有加载时要先载入该servlet
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
} // Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
//加载servlet,接下来继续分析loadServlet()方法
instance = loadServlet();
newInstance = true;
//类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
if (!instanceInitialized) {
//初始化servlet
initServlet(instance);
}
}
} //新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
} //SingleThreadedModel类型的servlet时返回instancePool中的一个instance。
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}

接下来看一下servlet的load过程loadServlet:

public synchronized Servlet loadServlet() throws ServletException {

    // Nothing to do if we already have an instance or an instance pool
// 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。
if (!singleThreadModel && (instance != null))
return instance; ...... Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
} //Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
......
} ...... if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
//此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值
singleThreadModel = true;
} //初始化刚加载的servlet
initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet; }

通过以上分析,我们知道了一个request请求是如何从Engine容器一路流动到了具体处理容器Wrapper中的,就是通过管道和阀的工作机制实现的,每一个容器都会对应一个管道,可以向管道中添加任意数量的阀valve,但必须要有一个基础阀,上一层的容器通过调用下一次容器的管道的阀的invoke方法实现request对象的传递。

tomcat源码 Container的更多相关文章

  1. Tomcat源码分析

    前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, ...

  2. Tomcat源码分析之—组件启动实现分析

    Tomcat由多个组件组成,那么Tomcat是怎么对他们的生命周期进行管理的么,这里将从Tomcat源码去分析其生命周期的实现: Bootstrape类为Tomcat的入口,所有的组件够通过实现Lif ...

  3. Tomcat源码分析--转

    一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...

  4. TOMCAT源码分析(转)

    前言:   本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教!建议:   毕竟TOMCAT的框架还是比较复杂的, 单是从文字上 ...

  5. Tomcat源码解析-整体流程介绍

    一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...

  6. Tomcat源码分析——Session管理分析(下)

    前言 在<TOMCAT源码分析——SESSION管理分析(上)>一文中我介绍了Session.Session管理器,还以StandardManager为例介绍了Session管理器的初始化 ...

  7. Tomcat源码分析——Session管理分析(上)

    前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...

  8. Tomcat源码分析——请求原理分析(下)

    前言 本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过<TOMCAT源码分析——请求原理分析(上)>和<TOMCAT源码分析——请求原理分析(中)>.在& ...

  9. Tomcat源码分析——请求原理分析(中)

    前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原 ...

随机推荐

  1. 10.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

    我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形. 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 是不是发现看不懂,哈哈:编程题就是这样,一定要归纳,手写过程: n ...

  2. 句法分析工具 LTP HanLP

    参考:http://cslt.riit.tsinghua.edu.cn/mediawiki/images/e/e5/%E5%8F%A5%E6%B3%95%E5%B7%A5%E5%85%B7%E5%88 ...

  3. matlab学习(1)strsplit与strtok

    strsplit函数用法: <1>默认使用空格符分割,返回一个cell数组 <2>也可以指定第二个参数进行分割 <3>第二个参数也可以时包含多个分隔符的元胞数组 & ...

  4. 基于C#利用ffmpeg提取视频帧

    利用ffmepg提取视频帧实际上是利用C#调用ffmepg命令行进行处理对应的视频,然后输出出视频帧 GetPicFromVideo("); static public string Get ...

  5. 2017.5.10 MapReduce内部逻辑

    MapReduce内部逻辑 Split:HDFS 中的数据以 Split 方式作为 MapReduce 的输入 Block 是 HDFS 术语,Split 是 MapReduce 术语 通常1个 Sp ...

  6. map遍历的几种方式和效率问题

    一.map遍历的效率 先创建一个map,添加好数据: Map<String, String> map = new HashMap<>();for (int i = 0; i & ...

  7. 简单说明 Virtual DOM 为啥快

    Virtual DOM 就是用 JS 的对象来描述 DOM 结构的一个 DOM 树.如: var element = { tagName: 'ul', // 节点标签名 props: { // DOM ...

  8. python可变类型和不可变类型

    原文地址:http://www.cnblogs.com/huamingao/p/5809936.html 可变类型 Vs 不可变类型 可变类型(mutable):列表,字典 不可变类型(unmutab ...

  9. unzip命令

    unzip命令用于解压缩由zip命令压缩的“.zip”压缩包. 语法 unzip(选项)(参数) 选项 -c:将解压缩的结果显示到屏幕上,并对字符做适当的转换: -f:更新现有的文件: -l:显示压缩 ...

  10. .NET本质论 用类型编程

    运行时的类型 类型本身并不是万能的.类型真正有意思的地方在于,程序员使用类型的实例,并让它们相互作用.类型的实例(instance)既可以是对象,也可以是值,这取决于类型如何定义的.基本数据类型(pr ...