Tomcat请求处理过程

目录(?)[+]

前面已经分析完了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方法进行处理,下面我们就来看看此方法的代码,为了节省版面,只保留与本文相关的代码。

[java]  view plain  copy
  1. protected boolean processSocket(Socket socket) {  
  2.         // Process the request from this socket  
  3.         try {  
  4.             SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  
  5.             wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());  
  6.             // During shutdown, executor may be null - avoid NPE  
  7.             if (!running) {  
  8.                 return false;  
  9.             }  
  10.             getExecutor().execute(new SocketProcessor(wrapper));  
  11.         } catch (RejectedExecutionException x) {  
  12.          //exception handler ...  
  13.          return false;  
  14.         }  
  15.         return true;  
  16. }  

通过上面的代码,我们可以看出首先将Socket封装为SocketWrapper,然后通过SocketProcessor来进行处理,因为Tomcat必然面对用户并发请求,因此这里Socket的处理通过新的线程池来处理。接下来我们再来看看SocketProcess的代码,同样省略了一些非核心的代码,代码如下:

[java]  view plain  copy
  1. <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>  
[java]  view plain  copy
  1. public void run() {  
  2.         boolean launch = false;  
  3.         synchronized (socket) {  
  4.             try {  
  5.                 SocketState state = SocketState.OPEN;  
  6.   
  7.                 try {  
  8.                     // SSL handshake  
  9.                     serverSocketFactory.handshake(socket.getSocket());  
  10.                 } catch (Throwable t) {  
  11.                     ExceptionUtils.handleThrowable(t);  
  12.                     if (log.isDebugEnabled()) {  
  13.                         log.debug(sm.getString("endpoint.err.handshake"), t);  
  14.                     }  
  15.                     // Tell to close the socket  
  16.                     state = SocketState.CLOSED;  
  17.                 }  
  18.   
  19.                 if ((state != SocketState.CLOSED)) {  
  20.                     if (status == null) {  
  21.                         // 1   
  22.                         state = handler.process(socket, SocketStatus.OPEN);  
  23.                     } else {  
  24.                         state = handler.process(socket,status);  
  25.                     }  
  26.                 }  
  27.                 if (state == SocketState.CLOSED) {  
  28.                     // Close socket  
  29.                     if (log.isTraceEnabled()) {  
  30.                         log.trace("Closing socket:"+socket);  
  31.                     }  
  32.                     countDownConnection();  
  33.                     try {  
  34.                         socket.getSocket().close();  
  35.                     } catch (IOException e) {  
  36.                         // Ignore  
  37.                     }  
  38.                 } else if (state == SocketState.OPEN ||  
  39.                         state == SocketState.UPGRADING  ||  
  40.                         state == SocketState.UPGRADED){  
  41.                     socket.setKeptAlive(true);  
  42.                     socket.access();  
  43.                     launch = true;  
  44.                 } else if (state == SocketState.LONG) {  
  45.                     socket.access();  
  46.                     waitingRequests.add(socket);  
  47.                 }  
  48.             } finally {  
  49.                //other code  
  50.             }  
  51.         }  
  52.         socket = null;  
  53.         // Finish up this request  
  54.     }  
  55.   
  56. }  
默认情况下,代码会运行到标注1的地方,标注1的地方又通过 org.apache.tomcat.util.net.JIoEndpoint.Handler#process 的方法进行处理,而通过前面Tomcat启动的文章,我们已经知道handler属性是在 org.apache.coyote.http11.Http11Protocol 的构造方法中初始化的,构造方法如下:

[java]  view plain  copy
  1. public Http11Protocol() {  
  2.     endpoint = new JIoEndpoint();  
  3.     cHandler = new Http11ConnectionHandler(this);  
  4.     ((JIoEndpoint) endpoint).setHandler(cHandler);  
  5.     setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);  
  6.     setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);  
  7.     setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);  
  8. }  


从构造方法中,我们可以清楚的看到,其实初始化了org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler的实例,那么接下来我们就来看看它的process方法,因为Http11ConnectionHandler继承了org.apache.coyote.AbstractProtocol.AbstractConnectionHandler,而自己没有实现process方法,因此会调用到父类的process方法,那么接下来我们就来看看AbstractConnectionHandler的process方法,代码如下:

[java]  view plain  copy
  1. public SocketState process(SocketWrapper<S> socket,  
  2.         SocketStatus status) {  
  3.     Processor<S> processor = connections.remove(socket.getSocket());  
  4.   
  5.     if (status == SocketStatus.DISCONNECT && processor == null) {  
  6.         //nothing more to be done endpoint requested a close  
  7.         //and there are no object associated with this connection  
  8.         return SocketState.CLOSED;  
  9.     }  
  10.   
  11.     socket.setAsync(false);  
  12.   
  13.     try {  
  14.         if (processor == null) {  
  15.             processor = recycledProcessors.poll();  
  16.         }  
  17.         if (processor == null) {  
  18.             processor = createProcessor();  
  19.         }  
  20.   
  21.         initSsl(socket, processor);  
  22.   
  23.         SocketState state = SocketState.CLOSED;  
  24.         do {  
  25.             if (status == SocketStatus.DISCONNECT &&  
  26.                     !processor.isComet()) {  
  27.                 // Do nothing here, just wait for it to get recycled  
  28.                 // Don't do this for Comet we need to generate an end  
  29.                 // event (see BZ 54022)  
  30.             } else if (processor.isAsync() ||  
  31.                     state == SocketState.ASYNC_END) {  
  32.                 state = processor.asyncDispatch(status);  
  33.             } else if (processor.isComet()) {  
  34.                 state = processor.event(status);  
  35.             } else if (processor.isUpgrade()) {  
  36.                 state = processor.upgradeDispatch();  
  37.             } else {  
  38.                 state = processor.process(socket);  
  39.             }  
  40.   
  41.             if (state != SocketState.CLOSED && processor.isAsync()) {  
  42.                 state = processor.asyncPostProcess();  
  43.             }  
  44.   
  45.             if (state == SocketState.UPGRADING) {  
  46.                 // Get the UpgradeInbound handler  
  47.                 UpgradeInbound inbound = processor.getUpgradeInbound();  
  48.                 // Release the Http11 processor to be re-used  
  49.                 release(socket, processor, falsefalse);  
  50.                 // Create the light-weight upgrade processor  
  51.                 processor = createUpgradeProcessor(socket, inbound);  
  52.                 inbound.onUpgradeComplete();  
  53.             }  
  54.         } while (state == SocketState.ASYNC_END ||  
  55.                 state == SocketState.UPGRADING);  
  56.   
  57.         return state;  
  58.     } catch(java.net.SocketException e) {  
  59.               // exception handler     
  60.     }  
  61.   
  62.     return SocketState.CLOSED;  
  63. }  

通过查看上面的代码,默认一个新连接的情况下,会调用 org.apache.coyote.Processor#process 方法,而Processor的实例实在 org.apache.coyote.AbstractProtocol.AbstractConnectionHandler#createProcessor 中创建的,通过查看createProcessor代码,我们发现是创建了一个org.apache.coyote.http11.Http11Processor的实例,那么接下来,我们就来看看它的process方法,因为Http11Processor继承了AbstractHttp11Processor,最终其实调用的是AbstractHttp11Processor的process方法,代码如下:

[java]  view plain  copy
  1. public SocketState process(SocketWrapper<S> socketWrapper)  
  2.     throws IOException {  
  3.     RequestInfo rp = request.getRequestProcessor();  
  4.     rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);  
  5.   
  6.     // Setting up the I/O  
  7.     // 1   
  8.     setSocketWrapper(socketWrapper);  
  9.     getInputBuffer().init(socketWrapper, endpoint);  
  10.     getOutputBuffer().init(socketWrapper, endpoint);  
  11.   
  12.     // Flags  
  13.     error = false;  
  14.     keepAlive = true;  
  15.     comet = false;  
  16.     openSocket = false;  
  17.     sendfileInProgress = false;  
  18.     readComplete = true;  
  19.     if (endpoint.getUsePolling()) {  
  20.         keptAlive = false;  
  21.     } else {  
  22.         keptAlive = socketWrapper.isKeptAlive();  
  23.     }  
  24.   
  25.     if (disableKeepAlive()) {  
  26.         socketWrapper.setKeepAliveLeft(0);  
  27.     }  
  28.   
  29.     while (!error && keepAlive && !comet && !isAsync() &&  
  30.             upgradeInbound == null && !endpoint.isPaused()) {  
  31.   
  32.         // Parsing the request header  
  33.         try {  
  34.             setRequestLineReadTimeout();  
  35.             //2   
  36.             if (!getInputBuffer().parseRequestLine(keptAlive)) {  
  37.                 if (handleIncompleteRequestLineRead()) {  
  38.                     break;  
  39.                 }  
  40.             }  
  41.   
  42.             if (endpoint.isPaused()) {  
  43.                 // 503 - Service unavailable  
  44.                 response.setStatus(503);  
  45.                 error = true;  
  46.             } else {  
  47.                 // Make sure that connectors that are non-blocking during  
  48.                 // header processing (NIO) only set the start time the first  
  49.                 // time a request is processed.  
  50.                 if (request.getStartTime() < 0) {  
  51.                     request.setStartTime(System.currentTimeMillis());  
  52.                 }  
  53.                 keptAlive = true;  
  54.                 // Set this every time in case limit has been changed via JMX  
  55.                 request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());  
  56.                 // Currently only NIO will ever return false here  
  57.                 // 3  
  58.                 if (!getInputBuffer().parseHeaders()) {  
  59.                     // We've read part of the request, don't recycle it  
  60.                     // instead associate it with the socket  
  61.                     openSocket = true;  
  62.                     readComplete = false;  
  63.                     break;  
  64.                 }  
  65.                 if (!disableUploadTimeout) {  
  66.                     setSocketTimeout(connectionUploadTimeout);  
  67.                 }  
  68.             }  
  69.         } catch (IOException e) {  
  70.             if (getLog().isDebugEnabled()) {  
  71.                 getLog().debug(  
  72.                         sm.getString("http11processor.header.parse"), e);  
  73.             }  
  74.             error = true;  
  75.             break;  
  76.         } catch (Throwable t) {  
  77.             ExceptionUtils.handleThrowable(t);  
  78.             UserDataHelper.Mode logMode = userDataHelper.getNextMode();  
  79.             if (logMode != null) {  
  80.                 String message = sm.getString(  
  81.                         "http11processor.header.parse");  
  82.                 switch (logMode) {  
  83.                     case INFO_THEN_DEBUG:  
  84.                         message += sm.getString(  
  85.                                 "http11processor.fallToDebug");  
  86.                         //$FALL-THROUGH$  
  87.                     case INFO:  
  88.                         getLog().info(message);  
  89.                         break;  
  90.                     case DEBUG:  
  91.                         getLog().debug(message);  
  92.                 }  
  93.             }  
  94.             // 400 - Bad Request  
  95.             response.setStatus(400);  
  96.             adapter.log(request, response, 0);  
  97.             error = true;  
  98.         }  
  99.   
  100.         if (!error) {  
  101.             // Setting up filters, and parse some request headers  
  102.             rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);  
  103.             try {  
  104.                 prepareRequest();  
  105.             } catch (Throwable t) {  
  106.                 ExceptionUtils.handleThrowable(t);  
  107.                 if (getLog().isDebugEnabled()) {  
  108.                     getLog().debug(sm.getString(  
  109.                             "http11processor.request.prepare"), t);  
  110.                 }  
  111.                 // 400 - Internal Server Error  
  112.                 response.setStatus(400);  
  113.                 adapter.log(request, response, 0);  
  114.                 error = true;  
  115.             }  
  116.         }  
  117.   
  118.         if (maxKeepAliveRequests == 1) {  
  119.             keepAlive = false;  
  120.         } else if (maxKeepAliveRequests > 0 &&  
  121.                 socketWrapper.decrementKeepAlive() <= 0) {  
  122.             keepAlive = false;  
  123.         }  
  124.   
  125.         // Process the request in the adapter  
  126.         if (!error) {  
  127.             try {  
  128.                 // 4  
  129.                 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);  
  130.                 adapter.service(request, response);  
  131.                 // Handle when the response was committed before a serious  
  132.                 // error occurred.  Throwing a ServletException should both  
  133.                 // set the status to 500 and set the errorException.  
  134.                 // If we fail here, then the response is likely already  
  135.                 // committed, so we can't try and set headers.  
  136.                 if(keepAlive && !error) { // Avoid checking twice.  
  137.                     error = response.getErrorException() != null ||  
  138.                             (!isAsync() &&  
  139.                             statusDropsConnection(response.getStatus()));  
  140.                 }  
  141.                 setCometTimeouts(socketWrapper);  
  142.             } catch (InterruptedIOException e) {  
  143.                 error = true;  
  144.             } catch (HeadersTooLargeException e) {  
  145.                 error = true;  
  146.                 // The response should not have been committed but check it  
  147.                 // anyway to be safe  
  148.                 if (!response.isCommitted()) {  
  149.                     response.reset();  
  150.                     response.setStatus(500);  
  151.                     response.setHeader("Connection""close");  
  152.                 }  
  153.             } catch (Throwable t) {  
  154.                 ExceptionUtils.handleThrowable(t);  
  155.                 getLog().error(sm.getString(  
  156.                         "http11processor.request.process"), t);  
  157.                 // 500 - Internal Server Error  
  158.                 response.setStatus(500);  
  159.                 adapter.log(request, response, 0);  
  160.                 error = true;  
  161.             }  
  162.         }  
  163.   
  164.         // Finish the handling of the request  
  165.         rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);  
  166.   
  167.         if (!isAsync() && !comet) {  
  168.             if (error) {  
  169.                 // If we know we are closing the connection, don't drain  
  170.                 // input. This way uploading a 100GB file doesn't tie up the  
  171.                 // thread if the servlet has rejected it.  
  172.                 getInputBuffer().setSwallowInput(false);  
  173.             }  
  174.             endRequest();  
  175.         }  
  176.   
  177.         rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);  
  178.   
  179.         // If there was an error, make sure the request is counted as  
  180.         // and error, and update the statistics counter  
  181.         if (error) {  
  182.             response.setStatus(500);  
  183.         }  
  184.         request.updateCounters();  
  185.   
  186.         if (!isAsync() && !comet || error) {  
  187.             getInputBuffer().nextRequest();  
  188.             getOutputBuffer().nextRequest();  
  189.         }  
  190.   
  191.         if (!disableUploadTimeout) {  
  192.             if(endpoint.getSoTimeout() > 0) {  
  193.                 setSocketTimeout(endpoint.getSoTimeout());  
  194.             } else {  
  195.                 setSocketTimeout(0);  
  196.             }  
  197.         }  
  198.   
  199.         rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);  
  200.   
  201.         if (breakKeepAliveLoop(socketWrapper)) {  
  202.             break;  
  203.         }  
  204.     }  
  205.   
  206.     rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);  
  207.   
  208.     if (error || endpoint.isPaused()) {  
  209.         return SocketState.CLOSED;  
  210.     } else if (isAsync() || comet) {  
  211.         return SocketState.LONG;  
  212.     } else if (isUpgrade()) {  
  213.         return SocketState.UPGRADING;  
  214.     } else {  
  215.         if (sendfileInProgress) {  
  216.             return SocketState.SENDFILE;  
  217.         } else {  
  218.             if (openSocket) {  
  219.                 if (readComplete) {  
  220.                     return SocketState.OPEN;  
  221.                 } else {  
  222.                     return SocketState.LONG;  
  223.                 }  
  224.             } else {  
  225.                 return SocketState.CLOSED;  
  226.             }  
  227.         }  
  228.     }  
  229. }  

上面的代码有点长,但是经过分析,我们还是可以看清楚主干,我已经在代码中将主流程通过数字标注了,我们就来一一看看标注了数字的地方:

  1. 标注1的地方(第7行)将Socket的输入流和输出流通过InternalInputBuffer进行了包装,InternalInputBuffer是在Http11Processor的构造函数中初始化的。
  2. 标注2的地方(第35行)调用了InternalInputBuffer的parseRequesLine方法解析http请求的请求行。(关于http请求行和请求头请看下文解释)
  3. 标注3的地方(第57行)调用了InternalInputBuffer的prarseHeaders方法解析http请求的请求头。解析完了以后,会将http header保存在org.apache.tomcat.util.http.MimeHeaders
  4. 标注4的地方(第128行)调用了org.apache.coyote.Adapter#service方法,次方法就会最终调用到具体的Servlet.

对于Http请求行和请求头,大家可以看下面的例子:

[plain]  view plain  copy
  1. GET /contextpath/querystring HTTP/1.1  
  2.   
  3. Host: 127.0.0.1:8080  
  4.   
  5. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:23.0) Gecko/20100101 Firefox/23.0  
  6.   
  7. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  
  8.   
  9. Accept-Language: en-US,en;q=0.5  
  10.   
  11. Accept-Encoding: gzip, deflate  
  12.   
  13. 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  
  14.   
  15. Connection: keep-alive  
  16.   
  17. Cache-Control: max-age=0  

在上面的Http协议get请求中,其中请求行就是第一行,GET /contextpath/querystring HTTP/1.1,余下的都是请求头。这里面需要注意根据Http协议的要求,请求行末尾必须是CRLF,而请求行与请求头,以及请求头之间必须用空行隔开,而空行也必须只包含CRLF。对于Http协议请求头的规范可以参考这里

通过上面的描述,我们可以整理出如下的一个请求解析流程:

[plain]  view plain  copy
  1. org.apache.tomcat.util.net.JIoEndpoint.Acceptor#run  
  2. ->org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor#run(请求处理线程池中运行)  
  3. -->org.apache.coyote.AbstractProtocol.AbstractConnectionHandler#process  
  4. --->org.apache.coyote.http11.AbstractHttp11Processor#process  
  5. ---->org.apache.coyote.http11.InternalInputBuffer#parseRequestLine  
  6. ---->org.apache.coyote.http11.InternalInputBuffer#parseHeaders  
  7. ---->org.apache.catalina.connector.CoyoteAdapter#service  

如何转发到Servlet

上面我们说了一个请求过来是如何根据http协议解析Socket的数据,最终将生成org.apache.coyote.Requestorg.apache.coyote.Response,接下来我们就来看看request,reponse是如何一步步的进入最终的Servlet进行处理的。这一步的入口就是CoyoteAdapter的service方法。 接下来我们就来看看它的代码:


[java]  view plain  copy
  1. <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>  
[java]  view plain  copy
  1. public void service(org.apache.coyote.Request req,  
  2.                     org.apache.coyote.Response res)  
  3.     throws Exception {  
  4.   
  5.   
  6.     Request request = (Request) req.getNote(ADAPTER_NOTES);  
  7.     Response response = (Response) res.getNote(ADAPTER_NOTES);  
  8.   
  9.     //1   
  10.     if (request == null) {  
  11.   
  12.         // Create objects  
  13.         request = connector.createRequest();  
  14.         request.setCoyoteRequest(req);  
  15.         response = connector.createResponse();  
  16.         response.setCoyoteResponse(res);  
  17.   
  18.         // Link objects  
  19.         request.setResponse(response);  
  20.         response.setRequest(request);  
  21.   
  22.         // Set as notes  
  23.         req.setNote(ADAPTER_NOTES, request);  
  24.         res.setNote(ADAPTER_NOTES, response);  
  25.   
  26.         // Set query string encoding  
  27.         req.getParameters().setQueryStringEncoding  
  28.             (connector.getURIEncoding());  
  29.   
  30.     }  
  31.   
  32.     if (connector.getXpoweredBy()) {  
  33.         response.addHeader("X-Powered-By", POWERED_BY);  
  34.     }  
  35.   
  36.     boolean comet = false;  
  37.     boolean async = false;  
  38.   
  39.     try {  
  40.   
  41.         // Parse and set Catalina and configuration specific  
  42.         // request parameters  
  43.         req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());  
  44.         //2  
  45.         boolean postParseSuccess = postParseRequest(req, request, res, response);  
  46.         if (postParseSuccess) {  
  47.             //check valves if we support async  
  48.             request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());  
  49.             // Calling the container  
  50.             //3  
  51.             connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);  
  52.   
  53.             // other code  
  54.   
  55.         }  
  56.         // other code  
  57.   
  58.     } catch (IOException e) {  
  59.         // Ignore  
  60.     } finally {  
  61.         req.getRequestProcessor().setWorkerThreadName(null);  
  62.         // Recycle the wrapper request and response  
  63.         if (!comet && !async) {  
  64.             request.recycle();  
  65.             response.recycle();  
  66.         } else {  
  67.             // Clear converters so that the minimum amount of memory  
  68.             // is used by this processor  
  69.             request.clearEncoders();  
  70.             response.clearEncoders();  
  71.         }  
  72.     }  
  73.   
  74. }  

为了可以清楚的看到主流程,上面删除了一部分非主流程的代码,接下来我们逐一分析一下标注了数字的地方:

  1. 标注1的代码(第9行)将org.apache.coyote.Requestorg.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. 标注2(第44行)的代码调用了postParseRequest方法,这个方法里面做的事情非常多,但是最终都是为了根据Request对象找到对应的Host,Conext和Wrapper对象,也就是说最终要清楚这个请求应该由哪个Servlet来处理。
  3. 标注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包装类)目的。具体代码如下:

  
  
[java] view plain copy
  1. <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>  
[java] view plain copy
  1. connector.getMapper().map(serverName, decodedURI, version,  
  2.                                       request.getMappingData());  
  3.             request.setContext((Context) request.getMappingData().context);  
  4.             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对应,通过分析代码,我们可以得到如下的一个调用链。

[plain] view plain copy
  1. ->org.apache.catalina.core.StandardEngineValve#invoke  
  2. -->org.apache.catalina.valves.AccessLogValve#invoke  
  3. --->org.apache.catalina.valves.ErrorReportValve#invoke  
  4. ---->org.apache.catalina.core.StandardHostValve#invoke  
  5. ----->org.apache.catalina.authenticator.AuthenticatorBase#invoke  
  6. ------>org.apache.catalina.core.StandardContextValve#invoke  
  7. ------->org.apache.catalina.core.StandardWrapperValve#invoke  
上述的调用栈中,最后会调用到StandardWrapperValve,它其实也是最终调用Servlet的地方,接下来我们就来看看它的代码:
[java]  view plain  copy
  1. public final void invoke(Request request, Response response)  
  2.     throws IOException, ServletException {  
  3.   
  4.     // Initialize local variables we may need  
  5.     boolean unavailable = false;  
  6.     Throwable throwable = null;  
  7.     // This should be a Request attribute...  
  8.     long t1=System.currentTimeMillis();  
  9.     requestCount++;  
  10.     StandardWrapper wrapper = (StandardWrapper) getContainer();  
  11.     Servlet servlet = null;  
  12.     Context context = (Context) wrapper.getParent();  
  13.   
  14.   
  15.     // Allocate a servlet instance to process this request  
  16.     try {  
  17.         //1  
  18.         if (!unavailable) {  
  19.             servlet = wrapper.allocate();  
  20.         }  
  21.     } catch (UnavailableException e) {  
  22.         container.getLogger().error(  
  23.                 sm.getString("standardWrapper.allocateException",  
  24.                         wrapper.getName()), e);  
  25.         long available = wrapper.getAvailable();  
  26.         if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  27.             response.setDateHeader("Retry-After", available);  
  28.             response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  29.                        sm.getString("standardWrapper.isUnavailable",  
  30.                                     wrapper.getName()));  
  31.         } else if (available == Long.MAX_VALUE) {  
  32.             response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  33.                        sm.getString("standardWrapper.notFound",  
  34.                                     wrapper.getName()));  
  35.         }  
  36.     } // other code  
  37.   
  38.     MessageBytes requestPathMB = request.getRequestPathMB();  
  39.     DispatcherType dispatcherType = DispatcherType.REQUEST;  
  40.     if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;  
  41.     request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);  
  42.     request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,  
  43.             requestPathMB);  
  44.     // Create the filter chain for this request  
  45.     ApplicationFilterFactory factory =  
  46.         ApplicationFilterFactory.getInstance();  
  47.     ApplicationFilterChain filterChain =  
  48.         factory.createFilterChain(request, wrapper, servlet);  
  49.   
  50.     // Reset comet flag value after creating the filter chain  
  51.     request.setComet(false);  
  52.   
  53.     // Call the filter chain for this request  
  54.     // NOTE: This also calls the servlet's service() method  
  55.     // 2   
  56.     try {  
  57.         if ((servlet != null) && (filterChain != null)) {  
  58.             // Swallow output if needed  
  59.             if (context.getSwallowOutput()) {  
  60.                 try {  
  61.                     SystemLogHandler.startCapture();  
  62.                     if (request.isAsyncDispatching()) {  
  63.                         //TODO SERVLET3 - async  
  64.                         ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();  
  65.                     } else if (comet) {  
  66.                         filterChain.doFilterEvent(request.getEvent());  
  67.                         request.setComet(true);  
  68.                     } else {  
  69.                         filterChain.doFilter(request.getRequest(),  
  70.                                 response.getResponse());  
  71.                     }  
  72.                 } finally {  
  73.                     String log = SystemLogHandler.stopCapture();  
  74.                     if (log != null && log.length() > 0) {  
  75.                         context.getLogger().info(log);  
  76.                     }  
  77.                 }  
  78.             } else {  
  79.                 if (request.isAsyncDispatching()) {  
  80.                     //TODO SERVLET3 - async  
  81.                     ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();  
  82.                 } else if (comet) {  
  83.                     request.setComet(true);  
  84.                     filterChain.doFilterEvent(request.getEvent());  
  85.                 } else {  
  86.                     filterChain.doFilter  
  87.                         (request.getRequest(), response.getResponse());  
  88.                 }  
  89.             }  
  90.   
  91.         }  
  92.     } catch(Exception e){  
  93.     // other code  
  94.     }  
  95.   
  96.   
  97. }  

为了节省版面,上面的代码已经删除非主流程的代码。接下来我们逐一分析一下标注了数字的地方:

  1. 标注1(第17行)的代码实例化了Servlet对象,在实例化的过程中使用了Java双检查锁的机制来实例化Servlet,有兴趣的童鞋可以去看看org.apache.catalina.core.StandardWrapper#allocate的代码。这里需要注意的是在Servlet2.4规范之前,有一个singleThreadMode模型,这个机制类似与之前EJB的无状态会话Bean机制,每个线程过来会通过实例池中取出一个实例来完成响应。在Servlet规范2.4之后,单线程模型已经被废除了。具体细节可以参考这里 .
  2. 标注2(第55行)的代码其实调用了大家熟悉的Servlet的过滤器链,过滤器链最终就会调用到Servlet.

最后,咋们再来看看过滤器滤链的处理,来看看org.apache.catalina.core.ApplicationFilterChain#doFilter,doFilter方法中会根据filterConfig中取的web.xml配置的过滤器,然后一个个调用,等每个过滤器执行完了以后,最终就会调用到Servlet的Service方法。

通过上面的分析,其实我们已经清楚了一个请求过来以后,Tomcat是如何一步步处理的。我们再来做一个总体的总结:

  1. 用户浏览器发送请求,请求会发送到对应的Connector监听的Socket端口。
  2. Connector从Socket流中获取数据,然后根据Http协议将其解析为Request和Reponse对象
  3. 找到Request对象对应的Host,Context,Wrapper
  4. 调用最终的Servelt的service进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值