作為Java開發(fā)人員來說Tomcat應(yīng)該在熟悉不過了,自己最近空閑時(shí)間比較多,前天突然很好奇到底用戶的請(qǐng)求是怎么被轉(zhuǎn)化成HttpServletRequest,所以自己想著看看源碼,但是因?yàn)榭紤]到一直以來自己對(duì) Tomcat的架構(gòu)并不熟悉,所以我決定在看源碼前好好的熟悉下Tomcat的架構(gòu),看看Tomcat都有哪些組件以及它們的作用分別是什么,這個(gè)過程我主要是參考官方文檔,另外就是Tomcat的源碼。
一、Tomcat的組成
關(guān)于Tomcat的架構(gòu)其實(shí)可以通過Tomcat下的server.xml做一個(gè)基本了解,在面試過程中有時(shí)候也會(huì)問到server.xml一些內(nèi)容,包括Tomcat的優(yōu)化其實(shí)很多都是在這里完成的,所以需要引起重視。
<!--?Prevent?memory?leaks?due?to?use?of?particular?java/javax?APIs-->
type="org.apache.catalina.UserDatabase"
description="User?database?that?can?be?updated?and?saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml"/>
connectionTimeout="20000"
redirectPort="8443"/>
resourceName="UserDatabase"/>
unpackWARs="true"autoDeploy="true">
prefix="localhost_access_log"suffix=".txt"
pattern="%h?%l?%u?%t?"%r"?%s?%b"/>
然后通過網(wǎng)上的一張架構(gòu)圖來了解各個(gè)層級(jí)之間的關(guān)系
tomcat整體架構(gòu).jpg
二、核心組件
結(jié)合這server.xml和上面這張圖,來簡(jiǎn)單的了解一下各個(gè)組件。
Server是Tomcat的頂層組件。在server.xml中Server元素表示整個(gè)Catalina Servlet容器。因此,它必須是server.xml配置文件中的單個(gè)最外層元素,簡(jiǎn)單一點(diǎn)可以將Server看做是Tomcat服務(wù)器本身,Server管理著整個(gè)Tomcat生命周期。一個(gè)Server內(nèi)可以包了多個(gè)listener,可以包含一個(gè)或多個(gè)Service。
Service是Tomcat的另一個(gè)頂層組件,Service元素由一個(gè)或多個(gè)Connector組件的組合,這些Connector共享一個(gè)Engine組件以處理傳入的請(qǐng)求。Service的功能就是對(duì)外提供服務(wù)。
接下來是Tomcat的幾個(gè)比較重要的組件:
Connector顧名思義就是連接器,它連接的是用戶請(qǐng)求和容器,即用戶的請(qǐng)求到達(dá)Connector后,由它將用戶請(qǐng)求進(jìn)行包裝,然后傳遞給具體的容器Engine進(jìn)行處理,之后再通過Connector將結(jié)果進(jìn)行包裝轉(zhuǎn)換傳遞給用戶。Connector功能就是處理用戶的請(qǐng)求和響應(yīng)。Service中的Engine對(duì)外是不可見的,所有與Engine的交互必須先經(jīng)過Connector處理。目前Tomcat有三種Connector,它們主要區(qū)別在于支持的協(xié)議不同。最常見的就是支持http/1.1,另外還有支持http/2以及ajp(Apache Jserv Protocol,一種二進(jìn)制協(xié)議)。關(guān)于這個(gè)三種連接器上相關(guān)的屬性建議看下官方文檔,因?yàn)閮?nèi)容還是比較多的。在對(duì)Tomcat進(jìn)行優(yōu)化事很多時(shí)候都會(huì)需要修改Connector上的相關(guān)參數(shù),比如最大連接數(shù)、最大線程數(shù)量等等。所以這部分內(nèi)容是非常重要的,也是核心。
Engine代表的處理請(qǐng)求的入口。它接受并處理來自Connector的所有請(qǐng)求,并將完成的響應(yīng)返回給連接器,最終傳輸回客戶端。
Host是Engine的子容器,一個(gè)Engine內(nèi)可以有一個(gè)或多個(gè)Host,它表示的一個(gè)虛擬主機(jī),
Context表示在特定虛擬主機(jī)中運(yùn)行的Web應(yīng)用程序,每一個(gè)Web應(yīng)用程序都基于WAR文件或包含相應(yīng)解壓縮內(nèi)容的相應(yīng)目錄。每一個(gè)虛擬主機(jī)中可以包含多個(gè)Context。
在Service中還有一個(gè)很重要的組件就是Executor,Executor表示是Tomcat中的組件之間共享的線程池。?Tomcat已經(jīng)默認(rèn)為每個(gè)連接器都創(chuàng)建了一個(gè)線程池。自定義線程池可以供Connector共享,也可以和其他組件共享該線程池。
除了上面的核心組件之外,還有其他一些嵌套組件,比如server.xml中定義的Listener、Realm、GlobalNamingResources等。這里就不細(xì)述了。
下圖是一個(gè)用戶請(qǐng)求和響應(yīng)的流程示意圖,實(shí)際情況當(dāng)然要復(fù)雜許多。
請(qǐng)求流程.jpg
三、請(qǐng)求轉(zhuǎn)換
下面主要看看Connector是如何將用戶請(qǐng)求一步一步進(jìn)行轉(zhuǎn)換的,我們先看下對(duì)應(yīng)類的繼承體系
Connector.jpg
Connector封裝了兩個(gè)主要的成員變量,一個(gè)是ProtocolHandler,一個(gè)是Adapter,前者根據(jù)不同的協(xié)議有不同的實(shí)現(xiàn)類型,我們以Http11NioProtocol為例,其類的繼承體系如下
Http11NioProtocol.jpg
ProtocolHandler主要是處理網(wǎng)絡(luò)連接,將字節(jié)流封裝成 Request對(duì)象,再將Request 適配成 Servlet 處理ServletRequest?對(duì)象這幾個(gè)動(dòng)作,用組件封裝起來了,ProtocolHandler包括了三個(gè)組件:Endpoint、Processor、Adapter。
而Endpoint的創(chuàng)建則在其默認(rèn)構(gòu)造函數(shù)實(shí)現(xiàn),以Http11NioProtocol為例,其代碼如下:
publicHttp11NioProtocol(){
super(newNioEndpoint());
}
Endpoint主要用來處理底層的Socket網(wǎng)絡(luò)連接,在Endpoint具體實(shí)現(xiàn)類中里面有個(gè)SocketProcessor的內(nèi)部類,它負(fù)責(zé)將Endpoint接收到的Socket請(qǐng)求轉(zhuǎn)化成org.apache.coyote.Request請(qǐng)求。
代碼如下:
protectedclassSocketProcessorextendsSocketProcessorBase{
publicSocketProcessor(SocketWrapperBase<NioChannel>?socketWrapper,?SocketEvent?event){
super(socketWrapper,?event);
}
@Override
protectedvoiddoRun(){
NioChannel?socket?=?socketWrapper.getSocket();
SelectionKey?key?=?socket.getIOChannel().keyFor(socket.getSocketWrapper().getPoller().getSelector());
Poller?poller?=?NioEndpoint.this.poller;
if(poller?==null)?{
socketWrapper.close();
return;
}
try{
inthandshake?=?-1;
try{
if(key?!=null)?{
if(socket.isHandshakeComplete())?{
handshake?=0;
}elseif(event?==?SocketEvent.STOP?||?event?==?SocketEvent.DISCONNECT?||
event?==?SocketEvent.ERROR)?{
handshake?=?-1;
}else{
handshake?=?socket.handshake(key.isReadable(),?key.isWritable());
event?=?SocketEvent.OPEN_READ;
}
}
}catch(IOException?x)?{
handshake?=?-1;
if(log.isDebugEnabled())?log.debug("Error?during?SSL?handshake",x);
}catch(CancelledKeyException?ckx)?{
handshake?=?-1;
}
if(handshake?==0)?{
SocketState?state?=?SocketState.OPEN;
//?Process?the?request?from?this?socket
if(event?==null)?{
state?=?getHandler().process(socketWrapper,?SocketEvent.OPEN_READ);
}else{
state?=?getHandler().process(socketWrapper,?event);
}
if(state?==?SocketState.CLOSED)?{
poller.cancelledKey(key,?socketWrapper);
}
}elseif(handshake?==?-1)?{
poller.cancelledKey(key,?socketWrapper);
}elseif(handshake?==?SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
}elseif(handshake?==?SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
}catch(CancelledKeyException?cx)?{
poller.cancelledKey(key,?socketWrapper);
}catch(VirtualMachineError?vme)?{
ExceptionUtils.handleThrowable(vme);
}catch(Throwable?t)?{
log.error(sm.getString("endpoint.processing.fail"),?t);
poller.cancelledKey(key,?socketWrapper);
}finally{
socketWrapper?=null;
event?=null;
//return?to?cache
if(running?&&?!paused?&&?processorCache?!=null)?{
processorCache.push(this);
}
}
}
}
代碼中g(shù)etHandler().process()方法會(huì)創(chuàng)建一個(gè)Processor對(duì)象,因?yàn)榇a較多,我只粘貼一部分:
publicSocketStateprocess(SocketWrapperBase<S>?wrapper,?SocketEvent?status){
if(wrapper?==null)?{
returnSocketState.CLOSED;
}
S?socket?=?wrapper.getSocket();
Processor?processor?=?connections.get(socket);
....
if(processor?==null)?{
String?negotiatedProtocol?=?wrapper.getNegotiatedProtocol();
if(negotiatedProtocol?!=null&&?negotiatedProtocol.length()?>0)?{
UpgradeProtocol?upgradeProtocol?=?getProtocol().getNegotiatedProtocol(negotiatedProtocol);
if(upgradeProtocol?!=null)?{
processor?=?upgradeProtocol.getProcessor(wrapper,?getProtocol().getAdapter());
}elseif(negotiatedProtocol.equals("http/1.1"))?{
}else{
if(getLog().isDebugEnabled())?{
getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",negotiatedProtocol));
}
returnSocketState.CLOSED;
}
}
}
if(processor?==null)?{
processor?=?recycledProcessors.pop();
if(getLog().isDebugEnabled())?{
getLog().debug(sm.getString("abstractConnectionHandler.processorPop",processor));
}
}
if(processor?==null)?{
processor?=?getProtocol().createProcessor();
register(processor);
}
//?省略代碼
......
}
上面的代碼在AbstractProtocol的內(nèi)部類ConnectionHandler中,其中socket變量其實(shí)是一個(gè)NioChannel實(shí)例。這個(gè)方法代碼中會(huì)根據(jù)不同情形創(chuàng)建相關(guān)的Processor。比如我這里會(huì)獲取相應(yīng)的AbstractHttp11Protocol然后調(diào)用其createProcessor()方法創(chuàng)建Http11Processor對(duì)象。
@Override
protectedProcessorcreateProcessor(){
Http11Processor?processor?=newHttp11Processor(this,?adapter);
returnprocessor;
}
而adapter其實(shí)在Connector初始化的時(shí)候就創(chuàng)建完成了,并將其添加到protocolHandler中,下面是Connector的initInternal()方法:
@Override
protectedvoidinitInternal()throwsLifecycleException{
super.initInternal();
.......
//?Initialize?adapter
adapter?=newCoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if(service?!=null)?{
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}
......
另外需要關(guān)注的一點(diǎn)是創(chuàng)建Http11Processor的時(shí)候,會(huì)調(diào)用其父類的構(gòu)造函數(shù):
publicHttp11Processor(AbstractHttp11Protocol<?>?protocol,?Adapter?adapter){
super(adapter);
this.protocol?=?protocol;
httpParser?=newHttpParser(protocol.getRelaxedPathChars(),
protocol.getRelaxedQueryChars());
inputBuffer?=newHttp11InputBuffer(request,?protocol.getMaxHttpHeaderSize(),
protocol.getRejectIllegalHeaderName(),?httpParser);
request.setInputBuffer(inputBuffer);
outputBuffer?=newHttp11OutputBuffer(response,?protocol.getMaxHttpHeaderSize());
response.setOutputBuffer(outputBuffer);
//?省略代碼
.....
}
而調(diào)用父類構(gòu)造函數(shù)的時(shí)候會(huì)創(chuàng)建兩個(gè)對(duì)象,Request和Response,對(duì)應(yīng)的具體類是org.apache.coyote.Request和org.apache.coyote.Response。這兩個(gè)對(duì)象下面還會(huì)使用到,不要混淆了。
回歸到正題,Processor創(chuàng)建完成后會(huì)調(diào)用它的process方法(AbstractProcessorLight.process)其方法內(nèi)部會(huì)調(diào)用具體的service方法,這里調(diào)用的是Http11Processor.service方法,這個(gè)方法內(nèi)內(nèi)容很多,我也并沒有仔細(xì)的看,應(yīng)該是對(duì)org.apache.coyote.Request和org.apache.coyote.Response進(jìn)行了參數(shù)設(shè)置,核心的地方在于調(diào)用adapter的service方法,代碼如下:
if(getErrorState().isIoAllowed())?{
try{
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
//?請(qǐng)求轉(zhuǎn)換request和response
getAdapter().service(request,?response);
if(keepAlive?&&?!getErrorState().isError()?&&?!isAsync()?&&
statusDropsConnection(response.getStatus()))?{
setErrorState(ErrorState.CLOSE_CLEAN,?null);
}
}catch(InterruptedIOException?e)?{
setErrorState(ErrorState.CLOSE_CONNECTION_NOW,?e);
}catch(HeadersTooLargeException?e)?{
log.error(sm.getString("http11processor.request.process"),?e);
if(response.isCommitted())?{
setErrorState(ErrorState.CLOSE_NOW,?e);
}else{
response.reset();
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN,?e);
response.setHeader("Connection","close");//TODO:Remove
}
}catch(Throwable?t)?{
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("http11processor.request.process"),?t);
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN,?t);
getAdapter().log(request,?response,0);
}
}
這里會(huì)調(diào)用CoyoteAdapter的service方法,代碼如下:
@Override
publicvoidservice(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);
if(request?==null)?{
request?=?connector.createRequest();
request.setCoyoteRequest(req);
response?=?connector.createResponse();
response.setCoyoteResponse(res);
request.setResponse(response);
response.setRequest(request);
req.setNote(ADAPTER_NOTES,?request);
res.setNote(ADAPTER_NOTES,?response);
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if(connector.getXpoweredBy())?{
response.addHeader("X-Powered-By",?POWERED_BY);
}
booleanasync=false;
boolean?postParseSuccess?=false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try{
postParseSuccess?=?postParseRequest(req,?request,?res,?response);
if(postParseSuccess)?{
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
connector.getService().getContainer().getPipeline().getFirst().invoke(
request,?response);
}
if(request.isAsync())?{
async=true;
ReadListener?readListener?=?req.getReadListener();
if(readListener?!=null&&?request.isFinished())?{
ClassLoader?oldCL?=null;
try{
oldCL?=?request.getContext().bind(false,null);
if(req.sendAllDataReadEvent())?{
req.getReadListener().onAllDataRead();
}
}finally{
request.getContext().unbind(false,?oldCL);
}
}
Throwable?throwable?=(Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if(!request.isAsyncCompleting()?&&?throwable?!=null)?{
request.getAsyncContextInternal().setErrorState(throwable,true);
}
}else{
request.finishRequest();
response.finishResponse();
}
}catch(IOException?e)?{
//?Ignore
}finally{
AtomicBoolean?error?=newAtomicBoolean(false);
res.action(ActionCode.IS_ERROR,?error);
if(request.isAsyncCompleting()?&&?error.get())?{
res.action(ActionCode.ASYNC_POST_PROCESS,null);
async=false;
}
//?Access?log
if(!async&&?postParseSuccess)?{
Context?context?=?request.getContext();
Host?host?=?request.getHost();
longtime?=?System.currentTimeMillis()?-?req.getStartTime();
if(context?!=null)?{
context.logAccess(request,?response,?time,false);
}elseif(response.isError())?{
if(host?!=null)?{
host.logAccess(request,?response,?time,false);
}else{
connector.getService().getContainer().logAccess(request,?response,?time,false);
}
}
}
req.getRequestProcessor().setWorkerThreadName(null);
if(!async)?{
updateWrapperErrorCount(request,?response);
request.recycle();
response.recycle();
}
}
}
這個(gè)方法的入?yún)閛rg.apache.coyote.Request和org.apache.coyote.Response兩個(gè)變量,方法內(nèi)會(huì)根據(jù)入?yún)?chuàng)建出org.apache.catalina.connector.Request和org.apache.catalina.connector.Response,而這兩個(gè)對(duì)象分別繼承了HttpServletRequest和HttpServletResponse,也就是說實(shí)際上用戶請(qǐng)求在這里完成了轉(zhuǎn)換,變成了我們非常熟悉的HttpServletRequest和HttpServletResponse。
然后執(zhí)行下面這段代碼:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);?
即通過Connector一步步將request和response丟給容器進(jìn)行處理,而且從方法名稱也可以看的出來,實(shí)際上是由Pipeline進(jìn)行處理的,這個(gè)就暫不深究了。也就是說到這里我們的HttpServletRequest和HttpServletResponse會(huì)被容器進(jìn)行處理了,在Connector的流程執(zhí)行完成,之后就是有容器進(jìn)行處理過程了,這里就不再繼續(xù)往下看了。
整個(gè)Connector執(zhí)行流程如下圖:
請(qǐng)求轉(zhuǎn)換流程圖.png
通過以上代碼和流程圖基本上就搞清楚了,用戶請(qǐng)求是如何變成我們熟悉的HttpServletRequest和HttpServletResponse的,當(dāng)然實(shí)際過程要復(fù)雜很多,我只是簡(jiǎn)單的通過跟蹤代碼了解了大概的過程,具體代碼內(nèi)容并沒有詳細(xì)的去看,后期如果有需要的話自己會(huì)挑選一部分進(jìn)行閱讀,比如今天的Connector這部分。
本次的學(xué)習(xí)收獲主要有兩點(diǎn)一是Tomcat的架構(gòu),即其組成及各組件的作用,當(dāng)然這部分沒有去深入學(xué)習(xí),二是請(qǐng)求轉(zhuǎn)換,即用戶請(qǐng)求過來之后是如何轉(zhuǎn)換成HttpServletRequest和HttpServletResponse的,這部分主要理清了大概的執(zhí)行流程,并簡(jiǎn)單的跟蹤了代碼,如果后期有需要會(huì)在來具體的分析這部分代碼。