Tomcat请求处理过程(Tomcat源代码解析五)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
前面已经分析完了Tomcat的启动和关闭过程。本篇就来接着分析一下Tomcat中请求的处理过程。
在開始本文之前,咋们首先来看看一个Http请求处理的过程。普通情况下是浏览器发送http请求->建立Socket连接->通过Socket读取数据->依据http协议解析数据->调用后台服务完毕响应
,具体的流程图如上图所看到的,等读者读完本篇,应该就清楚了上图所表达的意思。Tomcat既是一个HttpServer也是一个Servlet
容器,那么这里必定也涉及到如上过程,首先依据HTTP协议规范解析请求数据,然后将请求转发给Servlet进行处理。因此顺应这种思路,本文也将从Http协议请求解析,请求怎样转发给Servlet两个方面来进行分析。首先来看Http协议请求解析。
Http协议请求解析
在Tomcat启动过程(Tomcat源代码解析三)一文中,我们已经知道Tomcat启动以后。默认情况下会通过org.apache.tomcat.util.net.JIoEndpoint.Acceptor
监听Socket连接。当监听到有Socket连接的时候,就会调用org.apache.tomcat.util.net.JIoEndpoint#processSocket
方法进行处理,以下我们就来看看此方法的代码,为了节省版面,仅仅保留与本文相关的代码。
protected boolean processSocket(Socket socket) {
// Process the request from this socket
try {
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
// During shutdown, executor may be null - avoid NPE
if (!running) {
return false;
}
getExecutor().execute(new SocketProcessor(wrapper));
} catch (RejectedExecutionException x) {
//exception handler ...
return false;
}
return true;
}
通过上面的代码,我们能够看出首先将Socket封装为SocketWrapper,然后通过SocketProcessor来进行处理。由于Tomcat必定面对用户并发请求,因此这里Socket的处理通过新的线程池来处理。接下来我们再来看看SocketProcess的代码。相同省略了一些非核心的代码,代码例如以下:
<span style="color: rgb(102, 102, 102); font-family: 'Open Sans', HelveticaNeue-Light, 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12.6000003814697px; line-height: 25.2000007629395px; text-align: justify; background-color: rgb(236, 236, 236);">org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor#run</span>
public void run() {
boolean launch = false;
synchronized (socket) {
try {
SocketState state = SocketState.OPEN; try {
// SSL handshake
serverSocketFactory.handshake(socket.getSocket());
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.handshake"), t);
}
// Tell to close the socket
state = SocketState.CLOSED;
} if ((state != SocketState.CLOSED)) {
if (status == null) {
// 1
state = handler.process(socket, SocketStatus.OPEN);
} else {
state = handler.process(socket,status);
}
}
if (state == SocketState.CLOSED) {
// Close socket
if (log.isTraceEnabled()) {
log.trace("Closing socket:"+socket);
}
countDownConnection();
try {
socket.getSocket().close();
} catch (IOException e) {
// Ignore
}
} else if (state == SocketState.OPEN ||
state == SocketState.UPGRADING ||
state == SocketState.UPGRADED){
socket.setKeptAlive(true);
socket.access();
launch = true;
} else if (state == SocketState.LONG) {
socket.access();
waitingRequests.add(socket);
}
} finally {
//other code
}
}
socket = null;
// Finish up this request
} }
默认情况下。代码会执行到标注1的地方,标注1的地方又通过org.apache.tomcat.util.net.JIoEndpoint.Handler#process
的方法进行处理,而通过前面Tomcat启动的文章,我们已经知道handler属性是在org.apache.coyote.http11.Http11Protocol
的构造方法中初始化的,构造方法例如以下:
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
从构造方法中,我们能够清楚的看到。事实上初始化了org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler
的实例,那么接下来我们就来看看它的process方法,由于Http11ConnectionHandler继承了org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
,而自己没有实现process方法。因此会调用到父类的process方法,那么接下来我们就来看看AbstractConnectionHandler的process方法,代码例如以下:
public SocketState process(SocketWrapper<S> socket,
SocketStatus status) {
Processor<S> processor = connections.remove(socket.getSocket()); if (status == SocketStatus.DISCONNECT && processor == null) {
//nothing more to be done endpoint requested a close
//and there are no object associated with this connection
return SocketState.CLOSED;
} socket.setAsync(false); try {
if (processor == null) {
processor = recycledProcessors.poll();
}
if (processor == null) {
processor = createProcessor();
} initSsl(socket, processor); SocketState state = SocketState.CLOSED;
do {
if (status == SocketStatus.DISCONNECT &&
!processor.isComet()) {
// Do nothing here, just wait for it to get recycled
// Don't do this for Comet we need to generate an end
// event (see BZ 54022)
} else if (processor.isAsync() ||
state == SocketState.ASYNC_END) {
state = processor.asyncDispatch(status);
} else if (processor.isComet()) {
state = processor.event(status);
} else if (processor.isUpgrade()) {
state = processor.upgradeDispatch();
} else {
state = processor.process(socket);
} if (state != SocketState.CLOSED && processor.isAsync()) {
state = processor.asyncPostProcess();
} if (state == SocketState.UPGRADING) {
// Get the UpgradeInbound handler
UpgradeInbound inbound = processor.getUpgradeInbound();
// Release the Http11 processor to be re-used
release(socket, processor, false, false);
// Create the light-weight upgrade processor
processor = createUpgradeProcessor(socket, inbound);
inbound.onUpgradeComplete();
}
} while (state == SocketState.ASYNC_END ||
state == SocketState.UPGRADING); return state;
} catch(java.net.SocketException e) {
// exception handler
} return SocketState.CLOSED;
}
通过查看上面的代码。默认一个新连接的情况下。会调用org.apache.coyote.Processor#process
方法,而Processor的实例实在org.apache.coyote.AbstractProtocol.AbstractConnectionHandler#createProcessor
中创建的。通过查看createProcessor代码。我们发现是创建了一个org.apache.coyote.http11.Http11Processor的实例,那么接下来,我们就来看看它的process方法,由于Http11Processor继承了AbstractHttp11Processor,终于事实上调用的是AbstractHttp11Processor的process方法。代码例如以下:
public SocketState process(SocketWrapper<S> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // Setting up the I/O
// 1
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint); // Flags
error = false;
keepAlive = true;
comet = false;
openSocket = false;
sendfileInProgress = false;
readComplete = true;
if (endpoint.getUsePolling()) {
keptAlive = false;
} else {
keptAlive = socketWrapper.isKeptAlive();
} if (disableKeepAlive()) {
socketWrapper.setKeepAliveLeft(0);
} while (!error && keepAlive && !comet && !isAsync() &&
upgradeInbound == null && !endpoint.isPaused()) { // Parsing the request header
try {
setRequestLineReadTimeout();
//2
if (!getInputBuffer().parseRequestLine(keptAlive)) {
if (handleIncompleteRequestLineRead()) {
break;
}
} if (endpoint.isPaused()) {
// 503 - Service unavailable
response.setStatus(503);
error = true;
} else {
// Make sure that connectors that are non-blocking during
// header processing (NIO) only set the start time the first
// time a request is processed.
if (request.getStartTime() < 0) {
request.setStartTime(System.currentTimeMillis());
}
keptAlive = true;
// Set this every time in case limit has been changed via JMX
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
// Currently only NIO will ever return false here
// 3
if (!getInputBuffer().parseHeaders()) {
// We've read part of the request, don't recycle it
// instead associate it with the socket
openSocket = true;
readComplete = false;
break;
}
if (!disableUploadTimeout) {
setSocketTimeout(connectionUploadTimeout);
}
}
} catch (IOException e) {
if (getLog().isDebugEnabled()) {
getLog().debug(
sm.getString("http11processor.header.parse"), e);
}
error = true;
break;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
UserDataHelper.Mode logMode = userDataHelper.getNextMode();
if (logMode != null) {
String message = sm.getString(
"http11processor.header.parse");
switch (logMode) {
case INFO_THEN_DEBUG:
message += sm.getString(
"http11processor.fallToDebug");
//$FALL-THROUGH$
case INFO:
getLog().info(message);
break;
case DEBUG:
getLog().debug(message);
}
}
// 400 - Bad Request
response.setStatus(400);
adapter.log(request, response, 0);
error = true;
} if (!error) {
// Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
prepareRequest();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString(
"http11processor.request.prepare"), t);
}
// 400 - Internal Server Error
response.setStatus(400);
adapter.log(request, response, 0);
error = true;
}
} if (maxKeepAliveRequests == 1) {
keepAlive = false;
} else if (maxKeepAliveRequests > 0 &&
socketWrapper.decrementKeepAlive() <= 0) {
keepAlive = false;
} // Process the request in the adapter
if (!error) {
try {
// 4
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
adapter.service(request, response);
// Handle when the response was committed before a serious
// error occurred. Throwing a ServletException should both
// set the status to 500 and set the errorException.
// If we fail here, then the response is likely already
// committed, so we can't try and set headers.
if(keepAlive && !error) { // Avoid checking twice.
error = response.getErrorException() != null ||
(!isAsync() &&
statusDropsConnection(response.getStatus()));
}
setCometTimeouts(socketWrapper);
} catch (InterruptedIOException e) {
error = true;
} catch (HeadersTooLargeException e) {
error = true;
// The response should not have been committed but check it
// anyway to be safe
if (!response.isCommitted()) {
response.reset();
response.setStatus(500);
response.setHeader("Connection", "close");
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
getLog().error(sm.getString(
"http11processor.request.process"), t);
// 500 - Internal Server Error
response.setStatus(500);
adapter.log(request, response, 0);
error = true;
}
} // Finish the handling of the request
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); if (!isAsync() && !comet) {
if (error) {
// If we know we are closing the connection, don't drain
// input. This way uploading a 100GB file doesn't tie up the
// thread if the servlet has rejected it.
getInputBuffer().setSwallowInput(false);
}
endRequest();
} rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT); // If there was an error, make sure the request is counted as
// and error, and update the statistics counter
if (error) {
response.setStatus(500);
}
request.updateCounters(); if (!isAsync() && !comet || error) {
getInputBuffer().nextRequest();
getOutputBuffer().nextRequest();
} if (!disableUploadTimeout) {
if(endpoint.getSoTimeout() > 0) {
setSocketTimeout(endpoint.getSoTimeout());
} else {
setSocketTimeout(0);
}
} rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE); if (breakKeepAliveLoop(socketWrapper)) {
break;
}
} rp.setStage(org.apache.coyote.Constants.STAGE_ENDED); if (error || endpoint.isPaused()) {
return SocketState.CLOSED;
} else if (isAsync() || comet) {
return SocketState.LONG;
} else if (isUpgrade()) {
return SocketState.UPGRADING;
} else {
if (sendfileInProgress) {
return SocketState.SENDFILE;
} else {
if (openSocket) {
if (readComplete) {
return SocketState.OPEN;
} else {
return SocketState.LONG;
}
} else {
return SocketState.CLOSED;
}
}
}
}
上面的代码有点长。可是经过分析,我们还是能够看清楚主干。我已经在代码中将主流程通过数字标注了。我们就来一一看看标注了数字的地方:
- 标注1的地方(第7行)将Socket的输入流和输出流通过InternalInputBuffer进行了包装,InternalInputBuffer是在Http11Processor的构造函数中初始化的。
- 标注2的地方(第35行)调用了InternalInputBuffer的parseRequesLine方法解析http请求的请求行。
(关于http请求行和请求头请看下文解释)
- 标注3的地方(第57行)调用了InternalInputBuffer的prarseHeaders方法解析http请求的请求头。解析完了以后,会将http header保存在
org.apache.tomcat.util.http.MimeHeaders
- 标注4的地方(第128行)调用了org.apache.coyote.Adapter#service方法。次方法就会终于调用到详细的Servlet.
对于Http请求行和请求头,大家能够看以下的样例:
GET /contextpath/querystring HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:23.0) Gecko/20100101 Firefox/23.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: JSESSIONID=9F5897FEF3CDBCB234C050C132DCAE52; __atuvc=384%7C39; __utma=96992031.358732763.1380383869.1381468490.1381554710.38; __utmz=96992031.1380383869.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); Hm_lvt_21e144d0df165d6556d664e2836dadfe=1381330561,1381368826,1381395666,1381554711 Connection: keep-alive Cache-Control: max-age=0
在上面的Http协议get请求中,当中请求行就是第一行,GET /contextpath/querystring
,余下的都是请求头。
HTTP/1.1
这里面须要注意依据Http协议的要求,请求行末尾必须是CRLF,而请求行与请求头,以及请求头之间必须用空行隔开,而空行也必须仅仅包括CRLF。
对于Http协议请求头的规范能够參考这里。
通过上面的描写叙述,我们能够整理出例如以下的一个请求解析流程:
org.apache.tomcat.util.net.JIoEndpoint.Acceptor#run
->org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor#run(请求处理线程池中执行)
-->org.apache.coyote.AbstractProtocol.AbstractConnectionHandler#process
--->org.apache.coyote.http11.AbstractHttp11Processor#process
---->org.apache.coyote.http11.InternalInputBuffer#parseRequestLine
---->org.apache.coyote.http11.InternalInputBuffer#parseHeaders
---->org.apache.catalina.connector.CoyoteAdapter#service
怎样转发到Servlet
上面我们说了一个请求过来是怎样依据http协议解析Socket的数据。终于将生成org.apache.coyote.Request
和org.apache.coyote.Response
。接下来我们就来看看request,reponse是怎样一步步的进入终于的Servlet进行处理的。
这一步的入口就是CoyoteAdapter的service方法。
接下来我们就来看看它的代码:
<span style="color: rgb(102, 102, 102); font-family: 'Open Sans', HelveticaNeue-Light, 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12.6000003814697px; line-height: 25.2000007629395px; text-align: justify; background-color: rgb(236, 236, 236);">org.apache.catalina.connector.CoyoteAdapter#service</span>
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); //1
if (request == null) { // Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res); // Link objects
request.setResponse(response);
response.setRequest(request); // Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response); // Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding()); } if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
} boolean comet = false;
boolean async = false; try { // Parse and set Catalina and configuration specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
//2
boolean postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
//3
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); // other code }
// other code } catch (IOException e) {
// Ignore
} finally {
req.getRequestProcessor().setWorkerThreadName(null);
// Recycle the wrapper request and response
if (!comet && !async) {
request.recycle();
response.recycle();
} else {
// Clear converters so that the minimum amount of memory
// is used by this processor
request.clearEncoders();
response.clearEncoders();
}
} }
为了能够清楚的看到主流程,上面删除了一部分非主流程的代码。接下来我们逐一分析一下标注了数字的地方:
- 标注1的代码(第9行)将
org.apache.coyote.Request
和org.apache.coyote.Response
对象转变为org.apache.catalina.connector.Request
,org.apache.catalina.connector.Response
类型的对象。当中coyote包中的Request只不过包括了解析出来的http协议的数据,而connector包中的Request才是真正Servlet容器中的HttpServletRequest,它里面包括了完毕请求须要的host,context和wrapper信息,在这里每个wrapper事实上都相应web.xml配置的一个Servlet。 - 标注2(第44行)的代码调用了postParseRequest方法,这种方法里面做的事情许多,可是终于都是为了依据Request对象找到相应的Host,Conext和Wrapper对象,也就是说终于要清楚这个请求应该由哪个Servlet来处理。
- 标注3(第50)的代码将已经设置好了Host,Context,Wrapper对象的Request通过Pipeline机制链式传递给终于的Servlet。
上面仅仅是从总体上告诉了读者org.apache.catalina.connector.CoyoteAdapter#service
方法做的事情,接下来我们进一步分解每个步骤都详细做了哪些工作。第一步比較简单,大家能够自己阅读,我们关键来看2,3步。首先我们来看看postParseRequest方法。
通过分析org.apache.catalina.connector.CoyoteAdapter#postParseRequest的代码,我们会发现它终于是通过org.apache.tomcat.util.http.mapper.Mapper#map
方法来达到匹配请求到相应的Context和Wrapper(Servlet包装类)目的。详细代码例如以下:
<span style="color: rgb(102, 102, 102); font-family: 'Open Sans', HelveticaNeue-Light, 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12.6000003814697px; line-height: 25.2000007629395px; text-align: justify; background-color: rgb(236, 236, 236);">org.apache.catalina.connector.CoyoteAdapter#postParseRequest</span>connector.getMapper().map(serverName, decodedURI, version,
request.getMappingData());
request.setContext((Context) request.getMappingData().context);
request.setWrapper((Wrapper) request.getMappingData().wrapper);那我们再来看看此方法。通过分析它的代码。我们发现终于事实上是调用了几个
internalMap**
方法将找到的Context,Wrapper设置到org.apache.catalina.connector.Request对象的org.apache.tomcat.util.http.mapper.MappingData类型的属性中,map方法运行完以后。然后接下来就从MappingData中获取已经找到的Context和Wrapper。再设置到Request的context和wrapper中。接下来我们再来分析第3步。第3步通过pipeline链式调用机制终于调用了Servlet对象。而对于pipeline事实上是运用了责任链模式,它将各个阀门链接起来,然后一步步的调用,而至于有多少个阀门(Valve)对象。主要来源于两个地方,一个是conf/server.xml中配置的valve,我们知道全部的容器都是支持pipeline机制的,另外一个就是每个容器的构造当中自己初始化的阀门对象。
接下来一一看一下。对于StandardEngine来说有一个与之相应的StandardEngineValve。对于StandardHost有一个StandardHostValve与之相应。StandardContext有一个StandardContextValve与之相应。StandardWrapper与StandardWrapperValve相应,通过分析代码。我们能够得到例如以下的一个调用链。
->org.apache.catalina.core.StandardEngineValve#invoke
-->org.apache.catalina.valves.AccessLogValve#invoke
--->org.apache.catalina.valves.ErrorReportValve#invoke
---->org.apache.catalina.core.StandardHostValve#invoke
----->org.apache.catalina.authenticator.AuthenticatorBase#invoke
------>org.apache.catalina.core.StandardContextValve#invoke
------->org.apache.catalina.core.StandardWrapperValve#invoke上述的调用栈中。最后会调用到StandardWrapperValve,它事实上也是终于调用Servlet的地方,接下来我们就来看看它的代码:
public final void invoke(Request request, Response response)
throws IOException, ServletException { // Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount++;
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent(); // Allocate a servlet instance to process this request
try {
//1
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
} // other code MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet); // Reset comet flag value after creating the filter chain
request.setComet(false); // Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
// 2
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
//TODO SERVLET3 - async
((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
} else if (comet) {
filterChain.doFilterEvent(request.getEvent());
request.setComet(true);
} 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()) {
//TODO SERVLET3 - async
((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
} else if (comet) {
request.setComet(true);
filterChain.doFilterEvent(request.getEvent());
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
} }
} catch(Exception e){
// other code
} }
为了节省版面,上面的代码已经删除非主流程的代码。接下来我们逐一分析一下标注了数字的地方:
- 标注1(第17行)的代码实例化了Servlet对象,在实例化的过程中使用了Java双检查锁的机制来实例化Servlet。有兴趣的童鞋能够去看看org.apache.catalina.core.StandardWrapper#allocate的代码。这里须要注意的是在Servlet2.4规范之前,有一个singleThreadMode模型,这个机制类似与之前EJB的无状态会话Bean机制,每一个线程过来会通过实例池中取出一个实例来完毕响应。在Servlet规范2.4之后,单线程模型已经被废除了。详细细节能够參考这里 .
- 标注2(第55行)的代码事实上调用了大家熟悉的Servlet的过滤器链。过滤器链终于就会调用到Servlet.
最后,咋们再来看看过滤器滤链的处理。来看看org.apache.catalina.core.ApplicationFilterChain#doFilter
。doFilter方法中会依据filterConfig中取的web.xml配置的过滤器,然后一个个调用,等每一个过滤器运行完了以后,终于就会调用到Servlet的Service方法。
通过上面的分析,事实上我们已经清楚了一个请求过来以后,Tomcat是怎样一步步处理的。我们再来做一个整体的总结:
- 用户浏览器发送请求,请求会发送到相应的Connector监听的Socketport。
- Connector从Socket流中获取数据。然后依据Http协议将其解析为Request和Reponse对象
- 找到Request对象相应的Host,Context,Wrapper
- 调用终于的Servelt的service进行处理。
Tomcat请求处理过程(Tomcat源代码解析五)的更多相关文章
- Tomcat源代码解析系列
学web也有一段时间了.为了从底层了解web应用在Tomcat中的执行,决定看一下Tomcat的源代码參见<How Tomcat works> 和大牛博客.对大体架构有了一定的了解, ...
- Android源代码解析之(六)-->Log日志
转载请标明出处:一片枫叶的专栏 首先说点题外话,对于想学android framework源代码的同学,事实上能够在github中fork一份,详细地址:platform_frameworks_bas ...
- Android源代码解析之(七)-->LruCache缓存类
转载请标明出处:一片枫叶的专栏 android开发过程中常常会用到缓存.如今主流的app中图片等资源的缓存策略通常是分两级.一个是内存级别的缓存,一个是磁盘级别的缓存. 作为android系统的维护者 ...
- Android源代码解析之(十三)-->apk安装流程
转载请标明出处:一片枫叶的专栏 上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定文件夹.解析apk文件并安装的过程,这个安装过程实 ...
- Spring源代码解析
Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...
- Spring源代码解析(收藏)
Spring源代码解析(收藏) Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...
- Android源代码解析之(四)-->HandlerThread
转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了AsyncTast的基本使用以及实现原理,我们知道AsyncTask内部是通过线程池和Handler实现的.通过对线程池和handler的封装实现 ...
- Android源代码解析之(三)-->异步任务AsyncTask
转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了android中的异步消息机制. 主要解说了Handler对象的使用方式.消息的发送流程等.android的异步消息机制是android中多任务处 ...
- Tomcat架构解析(二)-----Connector、Tomcat启动过程以及Server的创建过程
Connector用于跟客户端建立连接,获取客户端的Socket,交由Container处理.需要解决的问题有监听.协议以及处理器映射等等. 一.Connector设计 Connector要实现的 ...
随机推荐
- django模型系统二
常用查询及表关系的实现 1.常用查询 每一个django模型类,都有一个默认的管理器 objects QuerySet表示数据库中对象的列表,它可以有0到多个过滤器.过滤器通过给定参数,缩小查询范围. ...
- JNI/NDK开发指南(九)——JNI调用性能測试及优化
转载请注明出处:http://blog.csdn.net/xyang81/article/details/44279725 在前面几章我们学习到了.在Java中声明一个native方法,然后生成本地接 ...
- apache wicket 7.X之HelloWorld
Wicket是什么 Wicket一个开发Java Web应用程序框架. 它使得开发web应用程序变得easy而轻松. Wicket利用一个POJO data beans组件使得它能够与不论什么持久层技 ...
- 在Google Drive上建立免费静态站点
现今建立一个属于自己的站点已经是一件非常普遍和简单的事情了. 你能够选择买空间,买域名.你也能够使用免费空间.免费域名.你能够选择动态的php wordpress,joomla或者是静态的站点(如使用 ...
- 兔子--ps中的基本工具总结(ps cs5)
矩形选框工具 椭圆选框工具 单行选框工具 单列选框工具 移动工具 套索工具柜 多边形套索工具 磁性套索工具 魔棒工具 高速选择工具 裁剪工具 切片工具 切片选择工具 吸管工具 颜色取样器工具 标尺工具 ...
- sqlserver 导入excel数据
有的时候需要将excel数据导入到数据库中,这里介绍一下操作方法: 1.可能需要安装sqlserver的插件 [AccessDatabaseEngine],这个可以在网上早,很多. 2.安装插件后,右 ...
- win7旗舰版怎么降级到专业版
一.操作准备及注意事项 1.UltraISO光盘制作工具9.5 2.备份C盘及桌面文件 二.win7旗舰版改成专业版的步骤 1.当前系统为Win7 SP1 64位旗舰版: 2.按Win+R打开运行,输 ...
- 前端项目中常用es6知识总结 -- Async、Await让异步美如画
项目开发中一些常用的es6知识,主要是为以后分享小程序开发.node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫. 项目开发常用es6介绍 1.块级作用域 let const 2. ...
- .less为后缀的文件是什么
.less为后缀的文件是什么 一.总结 1.less是什么:LESS 为 Web 开发者带来了福音,它在 CSS 的语法基础之上,引入了变量,Mixin(混入),运算以及函数等功能,大大简化了 CSS ...
- Visual Studio Team Services持续集成到Github仓库
Devops如何用VSTS持续集成到Github仓库! 工欲善其事,必先利其器.在开始正式的教程之前我们先来聊聊准备工作. 管理工具会VSTS. 代码管理会用GITHUB. 服务器会用Azure. ...