Container容器是所用servlet容器的父接口,也就是說作為一個(gè)servlet容器选脊,首先必須要實(shí)現(xiàn)Container接口逆济,每個(gè)tomcat服務(wù)器只能有唯一的根Container,Connector組件通過setContainer方法將Container容器和Connector關(guān)聯(lián)起來腿倚。共有四種類型Container容器囤萤,分別對(duì)應(yīng)不同概念的層次昼窗,每一層之間是父子的關(guān)系。
1阁将、Engine:整個(gè)Catalina servlet引擎,標(biāo)準(zhǔn)實(shí)現(xiàn)為StandardEngine膏秫。
2、Host:表示包含一個(gè)或多個(gè)Context容器的虛擬主機(jī),標(biāo)準(zhǔn)實(shí)現(xiàn)為StandardHost缤削。
3窘哈、Context:表示一個(gè)web應(yīng)用程序,一個(gè)Context可以有多個(gè)Wrapper亭敢,標(biāo)準(zhǔn)實(shí)現(xiàn)為StandardContext滚婉。
4、Wrapper:包裝一個(gè)獨(dú)立的Servlet容器帅刀,標(biāo)準(zhǔn)實(shí)現(xiàn)為StandardWrapper让腹。
在第二節(jié)的分析中我們知道,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>
那么這四種容器之間是如何協(xié)同工作的呢骇窍?Connector將一個(gè)連接請(qǐng)求交給Container之后,這四類容器之間如何分工合作锥余,怎么將請(qǐng)求交給特定的子容器進(jìn)行處理腹纳,即一個(gè)請(qǐng)求是如何從Engine最終映射到一個(gè)具體的servlet的?先介紹一下整體運(yùn)作流程驱犹,如下面的時(shí)序圖所示:
從上圖可以看出嘲恍,每個(gè)Container容器都有對(duì)應(yīng)的閥Valve,多個(gè)Valve組成了Pipeline雄驹,這就是Container的具體實(shí)現(xiàn)過程佃牛,也可以在server.xml文件中配置Pipeline和Valve的集合實(shí)現(xiàn)。管道Pipe包含了容器中要執(zhí)行的任務(wù)医舆,而每一個(gè)閥Valve表示一個(gè)具體的任務(wù)俘侠,在每個(gè)管道中,都會(huì)有一個(gè)默認(rèn)的閥蔬将,可以添加任意數(shù)量的閥兼贡,可通過server.xml文件配置。對(duì)過濾器熟悉的話就會(huì)發(fā)現(xiàn)娃胆,管道和閥的工作機(jī)制和過濾器工作機(jī)制相似遍希,Pipeline相當(dāng)于過濾器鏈FilterChain,Valve相當(dāng)于每一個(gè)過濾器Filter里烦。閥可以處理傳給它的request對(duì)象和response對(duì)象凿蒜,處理完一個(gè)Valve后接著處理下一個(gè)Valve,最后處理的閥是基礎(chǔ)閥胁黑。下面就追蹤每一個(gè)容器的管道废封,解析容器處理請(qǐng)求的流程
首先是Engine容器,默認(rèn)實(shí)現(xiàn)是StandardEngine丧蘸,創(chuàng)建StandardEngine時(shí)實(shí)例化其基礎(chǔ)閥漂洋,代碼如下
public StandardEngine() {
super();
//設(shè)置基礎(chǔ)閥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;
}
繼續(xù)跟蹤StandardEngineValve的invoke()方法,源碼為:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 選出和該request相關(guān)的Host,在MappingData中保存了請(qǐng)求和容器(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對(duì)應(yīng)的管道Pipeline刽漂,將request和response對(duì)象交給Host的閥去處理
host.getPipeline().getFirst().invoke(request, response);
StandardEngineValve的invoke()方法是在CoyoteAdapter類中調(diào)用的演训,也就是Connector將請(qǐng)求交給Container的過程:
//connector.getService().getContainer()得到Connector關(guān)聯(lián)的Container,然后將request和response對(duì)象交給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容器構(gòu)造器中設(shè)置了其基礎(chǔ)閥StandardHostValve
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
同樣跟蹤StandardHostValve的invoke方法
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 該request容器關(guān)聯(lián)的Context样悟,保存在MappingData中
Context context = request.getContext();
if (context == null) {
return;
}
//是否支持異步
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
try {
//設(shè)置StandardHostValve的類加載器
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
return;
}
// 將request傳遞給Context的閥去處理,有錯(cuò)誤的頁面必須在此處處理庭猩,不會(huì)繼續(xù)向下傳遞到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;
}
//設(shè)置錯(cuò)誤頁面
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的管道和閥的實(shí)現(xiàn)過程與Engine和Host完全一樣窟她,不再繼續(xù)分析。最后主要解析StandardHostValve的invoke()方法蔼水,看該方法如何將request交個(gè)一個(gè)servlet處理震糖。鑒于該方法源碼太長(zhǎng),只展示出了部分重要代碼趴腋。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
...
//獲取關(guān)聯(lián)的StandardWrapper
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
//wrapper的父容器Context
Context context = (Context) wrapper.getParent();
...
// 分配一個(gè)servlet實(shí)例處理該request
try {
//servlet可用時(shí)试伙,分配servlet,接下來會(huì)跟蹤allocate()方法
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
//分別設(shè)置了503錯(cuò)誤和404 not found
...
}
} catch (ServletException e) {
...
} catch (Throwable e) {
...
}
// 為該request設(shè)置過濾器
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 過濾器作用于該request于样,并且此過程中調(diào)用了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容器實(shí)例
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
...
}
...
}
接著跟蹤Wrapper的allocate源碼:該方法主要功能是分配一個(gè)初始化了的servlet實(shí)例,其service方法可以被調(diào)用潘靖。
public Servlet allocate() throws ServletException {
// servlet類沒有加載時(shí)剖出異常
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沒有加載時(shí)要先載入該servlet
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
...
//加載servlet穿剖,接下來繼續(xù)分析loadServlet()方法
instance = loadServlet();
newInstance = true;
//類加載之前并不知道該servlet是否為singleThreadModel,在loadServlet()中會(huì)改變singleThreadModel的值卦溢,所以此處要再判斷一次
if (!singleThreadModel) {
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
...
}
}
if (!instanceInitialized) {
//初始化servlet
initServlet(instance);
}
}
}
//新加載的servlet實(shí)現(xiàn)singleThreadModel時(shí)將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時(shí)返回instancePool中的一個(gè)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单寂,并且已經(jīng)存在一個(gè)instance實(shí)例時(shí)贬芥,不需要加載。
if (!singleThreadModel && (instance != null))
return instance;
...
Servlet servlet;
try {
...
//Context容器中的instanceManager宣决,是一個(gè)類加載器蘸劈,其newInstance方法根據(jù)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類后要重新判斷這個(gè)值
}
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;
}
通過以上分析尊沸,我們知道了一個(gè)request請(qǐng)求是如何從Engine容器一路流動(dòng)到了具體處理容器Wrapper中的威沫,就是通過管道和閥的工作機(jī)制實(shí)現(xiàn)的,每一個(gè)容器都會(huì)對(duì)應(yīng)一個(gè)管道洼专,可以向管道中添加任意數(shù)量的閥valve棒掠,但必須要有一個(gè)基礎(chǔ)閥,上一層的容器通過調(diào)用下一次容器的管道的閥的invoke方法實(shí)現(xiàn)request對(duì)象的傳遞屁商。
tomcat源碼分析(第一篇 tomcat源碼分析(第一篇 從整體架構(gòu)開始))
tomcat源碼分析(第二篇 tomcat啟動(dòng)過程詳解)
tomcat源碼分析(第三篇 tomcat請(qǐng)求原理解析--Connector源碼分析)