tomcat源码分析(第四篇 tomcat请求处理原理解析--C
Container容器是所用servlet容器的父接口,也就是说作为一个servlet容器,首先必须要实现Container接口,每个tomcat服务器只能有唯一的根Container,Connector组件通过setContainer方法将Container容器和Connector关联起来。共有四种类型Container容器,分别对应不同概念的层次,每一层之间是父子的关系。
1、Engine:整个Catalina servlet引擎,标准实现为StandardEngine。
2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。
3、Context:表示一个web应用程序,一个Context可以有多个Wrapper,标准实现为StandardContext。
4、Wrapper:包装一个独立的Servlet容器,标准实现为StandardWrapper。
在第二节的分析中我们知道,server.xml文件中配置了Engine和Host。
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
那么这四种容器之间是如何协同工作的呢?Connector将一个连接请求交给Container之后,这四类容器之间如何分工合作,怎么将请求交给特定的子容器进行处理,即一个请求是如何从Engine最终映射到一个具体的servlet的?先介绍一下整体运作流程,如下面的时序图所示:
image从上图可以看出,每个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 {
// 选出和该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());
}
// host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理
host.getPipeline().getFirst().invoke(request, response);
StandardEngineValve的invoke()方法是在CoyoteAdapter类中调用的,也就是Connector将请求交给Container的过程:
//connector.getService().getContainer()得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。
if (!request.isAsyncDispatching() && request.isAsync() &&
response.isErrorReportRequired()) {
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
if (request.isAsyncDispatching()) {
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (t != null) {
asyncConImpl.setErrorState(t, true);
}
}
同样Host容器构造器中设置了其基础阀StandardHostValve
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
同样跟踪StandardHostValve的invoke方法
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 该request容器关联的Context,保存在MappingData中
Context context = request.getContext();
if (context == null) {
return;
}
//是否支持异步
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
try {
//设置StandardHostValve的类加载器
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
return;
}
// 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中
try {
if (!response.isErrorReportRequired()) {
context.getPipeline().getFirst().invoke(request, response);
}
} 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;
}
//设置错误页面
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完全一样,不再继续分析。最后主要解析StandardHostValve的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();
...
// 分配一个servlet实例处理该request
try {
//servlet可用时,分配servlet,接下来会跟踪allocate()方法
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
//分别设置了503错误和404 not found
...
}
} catch (ServletException e) {
...
} catch (Throwable e) {
...
}
// 为该request设置过滤器
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 过滤器作用于该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) {
...
}
// 释放该request的过滤链
if (filterChain != null) {
filterChain.release();
}
// 回收servlet容器实例
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
...
}
...
}
接着跟踪Wrapper的allocate源码:该方法主要功能是分配一个初始化了的servlet实例,其service方法可以被调用。
public Servlet allocate() throws ServletException {
// 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) {
// servlet没有加载时要先载入该servlet
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
...
//加载servlet,接下来继续分析loadServlet()方法
instance = loadServlet();
newInstance = true;
//类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次
if (!singleThreadModel) {
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
...
}
}
if (!instanceInitialized) {
//初始化servlet
initServlet(instance);
}
}
}
//新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance
if (singleThreadModel) {
if (newInstance) {
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled()) {
log.trace("Returning non-STM instance");
}
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) {
...
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
接下来看一下servlet的load过程
public synchronized Servlet loadServlet() throws ServletException {
// 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。
if (!singleThreadModel && (instance != null))
return instance;
...
Servlet servlet;
try {
...
//Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
...
} catch (Throwable e) {
...
}
if (multipartConfigElement == null) {
MultipartConfig annotation =
servlet.getClass().getAnnotation(MultipartConfig.class);
if (annotation != null) {
multipartConfigElement =
new MultipartConfigElement(annotation);
}
}
// Special handling for ContainerServlet instances
// Note: The InstanceManager checks if the application is permitted
// to load ContainerServlets
if (servlet instanceof ContainerServlet) {
((ContainerServlet) servlet).setWrapper(this);
}
classLoadTime=(int) (System.currentTimeMillis() -t1);
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
singleThreadModel = true; //此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值
}
initServlet(servlet); //初始化刚加载的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源码分析(第一篇 tomcat源码分析(第一篇 从整体架构开始))
tomcat源码分析(第二篇 tomcat启动过程详解)
tomcat源码分析(第三篇 tomcat请求原理解析--Connector源码分析)