浅读Tomcat源码(三)---请求处理

来源:互联网 发布:交通安全事故数据 编辑:程序博客网 时间:2024/05/16 11:48
转载自:http://blog.csdn.net/qq_28241149/article/details/78434386

在上一篇中,我们讲述了Tomcat在启动之初所做的事情,我们简单罗列回顾一下

1)依次开启组件,其中较为核心的是开启Connector的serverSocket,并等待请求

2)开新线程初始化容器,补充一点,之所以要开启新线程而不在主线程完成,个人理解核心原因是为了在Tomcat运行过程中可以直接添加新的应用

3)开启等待关闭线程,等待Tomcat的关闭指令


这一篇我们将着重讲解Tomcat在接收到新的Http请求的时候应该做的事情


首先我们明白一点,Http协议是一种基于TCP连接的应用层协议,而Socket又是基于TCP的一种横跨网络层、传输层、应用层的接口,而在Tomcat中完成网络连接的代码也基本都是由Socket实现的。


在上一篇中我们看到了JIoEndpoint中开启了一个监听Http连接端口(默认8080)的serverSocket,当有请求的时候,serverSocket会接收到一个socket,并将socket传入processSocket方法中处理:

[java] view plain copy
print?
  1. Socket socket = null;  
  2. try {  
  3.     // Accept the next incoming connection from the server  
  4.     // socket  
  5.     socket = serverSocketFactory.acceptSocket(serverSocket);  
  6. catch (IOException ioe) {  
  7.     // Introduce delay if necessary  
  8.     errorDelay = handleExceptionWithDelay(errorDelay);  
  9.     // re-throw  
  10.     throw ioe;  
  11. }  
  12. // Successful accept, reset the error delay  
  13. errorDelay = 0;  
  14.   
  15.   
  16. // Configure the socket  
  17. if (setSocketOptions(socket)) {  
  18.     // Hand this socket off to an appropriate processor  
  19.     if (!processSocket(socket)) {  
  20.         // Close socket right away  
  21.         try {  
  22.             socket.close();  
  23.         } catch (IOException e) {  
  24.             // Ignore  
  25.         }  
  26.     } else {  
  27.         countUpConnection();  
  28.     }  
  29. else {  
  30.     // Close socket right away  
  31.     try {  
  32.         socket.close();  
  33.     } catch (IOException e) {  
  34.         // Ignore  
  35.     }  
  36. }  
下面看下processSocket方法:

[java] view plain copy
print?
  1. /** 
  2.      * Process a new connection from a new client. Wraps the socket so 
  3.      * keep-alive and other attributes can be tracked and then passes the socket 
  4.      * to the executor for processing. 
  5.      *  
  6.      * @param socket    The socket associated with the client. 
  7.      *  
  8.      * @return          <code>true</code> if the socket is passed to the 
  9.      *                  executor, <code>false</code> if something went wrong or 
  10.      *                  if the endpoint is shutting down. Returning 
  11.      *                  <code>false</code> is an indication to close the socket 
  12.      *                  immediately. 
  13.      */  
  14.     protected boolean processSocket(Socket socket) {  
  15.         // Process the request from this socket  
  16.         try {  
  17.             SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  
  18.             wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());  
  19.             // During shutdown, executor may be null - avoid NPE  
  20.             if (!running) {  
  21.                 return false;  
  22.             }  
  23.             getExecutor().execute(new SocketProcessor(wrapper));  
  24.         } catch (RejectedExecutionException x) {  
  25.             log.warn("Socket processing request was rejected for:"+socket,x);  
  26.             return false;  
  27.         } catch (Throwable t) {  
  28.             ExceptionUtils.handleThrowable(t);  
  29.             // This means we got an OOM or similar creating a thread, or that  
  30.             // the pool and its queue are full  
  31.             log.error(sm.getString("endpoint.process.fail"), t);  
  32.             return false;  
  33.         }  
  34.         return true;  
  35.     }  
这里我们看到代码将Socket封装进了SocketWrapper中,并开启新线程SocketProcessor去处理请求:

[java] view plain copy
print?
  1. /** 
  2.      * This class is the equivalent of the Worker, but will simply use in an 
  3.      * external Executor thread pool. 
  4.      */  
  5.     protected class SocketProcessor implements Runnable {  
  6.           
  7.         protected SocketWrapper<Socket> socket = null;  
  8.         protected SocketStatus status = null;  
  9.           
  10.         public SocketProcessor(SocketWrapper<Socket> socket) {  
  11.             if (socket==nullthrow new NullPointerException();  
  12.             this.socket = socket;  
  13.         }  
  14.   
  15.         public SocketProcessor(SocketWrapper<Socket> socket, SocketStatus status) {  
  16.             this(socket);  
  17.             this.status = status;  
  18.         }  
  19.   
  20.         @Override  
  21.         public void run() {  
  22.             boolean launch = false;  
  23.             synchronized (socket) {  
  24.                 try {  
  25.                     SocketState state = SocketState.OPEN;  
  26.   
  27.                     try {  
  28.                         // SSL handshake  
  29.                         serverSocketFactory.handshake(socket.getSocket());  
  30.                     } catch (Throwable t) {  
  31.                         ExceptionUtils.handleThrowable(t);  
  32.                         if (log.isDebugEnabled()) {  
  33.                             log.debug(sm.getString("endpoint.err.handshake"), t);  
  34.                         }  
  35.                         // Tell to close the socket  
  36.                         state = SocketState.CLOSED;  
  37.                     }  
  38.                           
  39.                     if ( (state != SocketState.CLOSED) ) {  
  40.                         state = (status==null)?handler.process(socket):handler.process(socket,status);  
  41.                     }  
  42.                     if (state == SocketState.CLOSED) {  
  43.                         // Close socket  
  44.                         if (log.isTraceEnabled()) {  
  45.                             log.trace("Closing socket:"+socket);  
  46.                         }  
  47.                         countDownConnection();  
  48.                         try {  
  49.                             socket.getSocket().close();  
  50.                         } catch (IOException e) {  
  51.                             // Ignore  
  52.                         }  
  53.                     } else if (state == SocketState.ASYNC_END ||  
  54.                             state == SocketState.OPEN){  
  55.                         socket.setKeptAlive(true);  
  56.                         socket.access();  
  57.                         launch = true;  
  58.                     } else if (state == SocketState.LONG) {  
  59.                         socket.access();  
  60.                         waitingRequests.add(socket);  
  61.                     }  
  62.                 } finally {  
  63.                     if (launch) {  
  64.                         try {  
  65.                             getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));  
  66.                         } catch (NullPointerException npe) {  
  67.                             if (running) {  
  68.                                 log.error(sm.getString("endpoint.launch.fail"),  
  69.                                         npe);  
  70.                             }  
  71.                         }  
  72.                     }  
  73.                 }  
  74.             }  
  75.             socket = null;  
  76.             // Finish up this request  
  77.         }  
  78.           
  79.     }  
这里调用了handler去处理Socket,handler有两个实现类,分别处理AJP和HTTP,我们看到HTTP的:

[java] view plain copy
print?
  1. @Override  
  2.         public SocketState process(SocketWrapper<Socket> socket, SocketStatus status) {  
  3.             Http11Processor processor = connections.remove(socket);  
  4.             try {  
  5.                 if (processor == null) {  
  6.                     processor = recycledProcessors.poll();  
  7.                 }  
  8.                 if (processor == null) {  
  9.                     processor = createProcessor();  
  10.                 }  
  11.   
  12.                 if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {  
  13.                     processor.setSSLSupport(  
  14.                             proto.sslImplementation.getSSLSupport(  
  15.                                     socket.getSocket()));  
  16.                 } else {  
  17.                     processor.setSSLSupport(null);  
  18.                 }  
  19.                   
  20.                 SocketState state = socket.isAsync()?processor.asyncDispatch(status):processor.process(socket);  
  21.                 if (state == SocketState.LONG) {  
  22.                     connections.put(socket, processor);  
  23.                     socket.setAsync(true);  
  24.                     // longPoll may change socket state (e.g. to trigger a  
  25.                     // complete or dispatch)  
  26.                     return processor.asyncPostProcess();  
  27.                 } else {  
  28.                     socket.setAsync(false);  
  29.                     recycledProcessors.offer(processor);  
  30.                 }  
  31.                 return state;  
  32.             } catch(java.net.SocketException e) {  
  33.                 // SocketExceptions are normal  
  34.                 log.debug(sm.getString(  
  35.                         "http11protocol.proto.socketexception.debug"), e);  
  36.             } catch (java.io.IOException e) {  
  37.                 // IOExceptions are normal  
  38.                 log.debug(sm.getString(  
  39.                         "http11protocol.proto.ioexception.debug"), e);  
  40.             }  
  41.             // Future developers: if you discover any other  
  42.             // rare-but-nonfatal exceptions, catch them here, and log as  
  43.             // above.  
  44.             catch (Throwable e) {  
  45.                 ExceptionUtils.handleThrowable(e);  
  46.                 // any other exception or error is odd. Here we log it  
  47.                 // with "ERROR" level, so it will show up even on  
  48.                 // less-than-verbose logs.  
  49.                 log.error(sm.getString("http11protocol.proto.error"), e);  
  50.             }  
  51.             recycledProcessors.offer(processor);  
  52.             return SocketState.CLOSED;  
  53.         }  

到这里会觉得Tomcat的调用过程怎么那么绕,不过有些经验的开发人员都能意识到这其中深含的模块化思想,目前对Tomcat源码只是初窥门径,我在后面的学习中会整理出Tomcat这些处理过程的模块分化,先继续就着调用的顺序往下看,processor的process过程:

[java] view plain copy
print?
  1. /** 
  2.      * Process pipelined HTTP requests on the specified socket. 
  3.      * 
  4.      * @param socketWrapper Socket from which the HTTP requests will be read 
  5.      *               and the HTTP responses will be written. 
  6.      *   
  7.      * @throws IOException error during an I/O operation 
  8.      */  
  9.     public SocketState process(SocketWrapper<Socket> socketWrapper)  
  10.         throws IOException {  
  11.         RequestInfo rp = request.getRequestProcessor();  
  12.         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);  
  13.   
  14.         // Set the remote address  
  15.         remoteAddr = null;  
  16.         remoteHost = null;  
  17.         localAddr = null;  
  18.         localName = null;  
  19.         remotePort = -1;  
  20.         localPort = -1;  
  21.   
  22.         // Setting up the I/O  
  23.         this.socket = socketWrapper;  
  24.         inputBuffer.setInputStream(socket.getSocket().getInputStream());  
  25.         outputBuffer.setOutputStream(socket.getSocket().getOutputStream());  
  26.   
  27.         // Error flag  
  28.         error = false;  
  29.         keepAlive = true;  
  30.   
  31.         int keepAliveLeft = maxKeepAliveRequests>0?socketWrapper.decrementKeepAlive():-1;  
  32.           
  33.         int soTimeout = endpoint.getSoTimeout();  
  34.   
  35.         try {  
  36.             socket.getSocket().setSoTimeout(soTimeout);  
  37.         } catch (Throwable t) {  
  38.             ExceptionUtils.handleThrowable(t);  
  39.             log.debug(sm.getString("http11processor.socket.timeout"), t);  
  40.             error = true;  
  41.         }  
  42.   
  43.         boolean keptAlive = socketWrapper.isKeptAlive();  
  44.   
  45.         while (!error && keepAlive && !endpoint.isPaused()) {  
  46.   
  47.             // Parsing the request header  
  48.             try {  
  49.                 //TODO - calculate timeout based on length in queue (System.currentTimeMills() - wrapper.getLastAccess() is the time in queue)  
  50.                 if (keptAlive) {  
  51.                     if (keepAliveTimeout > 0) {  
  52.                         socket.getSocket().setSoTimeout(keepAliveTimeout);  
  53.                     }  
  54.                     else if (soTimeout > 0) {  
  55.                         socket.getSocket().setSoTimeout(soTimeout);  
  56.                     }  
  57.                 }  
  58.                 inputBuffer.parseRequestLine(false);  
  59.                 request.setStartTime(System.currentTimeMillis());  
  60.                 keptAlive = true;  
  61.                 if (disableUploadTimeout) {  
  62.                     socket.getSocket().setSoTimeout(soTimeout);  
  63.                 } else {  
  64.                     socket.getSocket().setSoTimeout(connectionUploadTimeout);  
  65.                 }  
  66.                 inputBuffer.parseHeaders();  
  67.             } catch (IOException e) {  
  68.                 error = true;  
  69.                 break;  
  70.             } catch (Throwable t) {  
  71.                 ExceptionUtils.handleThrowable(t);  
  72.                 if (log.isDebugEnabled()) {  
  73.                     log.debug(sm.getString("http11processor.header.parse"), t);  
  74.                 }  
  75.                 // 400 - Bad Request  
  76.                 response.setStatus(400);  
  77.                 adapter.log(request, response, 0);  
  78.                 error = true;  
  79.             }  
  80.   
  81.             if (!error) {  
  82.                 // Setting up filters, and parse some request headers  
  83.                 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);  
  84.                 try {  
  85.                     prepareRequest();  
  86.                 } catch (Throwable t) {  
  87.                     ExceptionUtils.handleThrowable(t);  
  88.                     if (log.isDebugEnabled()) {  
  89.                         log.debug(sm.getString("http11processor.request.prepare"), t);  
  90.                     }  
  91.                     // 400 - Internal Server Error  
  92.                     response.setStatus(400);  
  93.                     adapter.log(request, response, 0);  
  94.                     error = true;  
  95.                 }  
  96.             }  
  97.   
  98.             if (maxKeepAliveRequests > 0 && keepAliveLeft == 0)  
  99.                 keepAlive = false;  
  100.   
  101.             // Process the request in the adapter  
  102.             if (!error) {  
  103.                 try {  
  104.                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);  
  105.                     adapter.service(request, response);  
  106.                     // Handle when the response was committed before a serious  
  107.                     // error occurred.  Throwing a ServletException should both  
  108.                     // set the status to 500 and set the errorException.  
  109.                     // If we fail here, then the response is likely already  
  110.                     // committed, so we can't try and set headers.  
  111.                     if(keepAlive && !error) { // Avoid checking twice.  
  112.                         error = response.getErrorException() != null ||  
  113.                                 statusDropsConnection(response.getStatus());  
  114.                     }  
  115.   
  116.                 } catch (InterruptedIOException e) {  
  117.                     error = true;  
  118.                 } catch (Throwable t) {  
  119.                     ExceptionUtils.handleThrowable(t);  
  120.                     log.error(sm.getString("http11processor.request.process"), t);  
  121.                     // 500 - Internal Server Error  
  122.                     response.setStatus(500);  
  123.                     adapter.log(request, response, 0);  
  124.                     error = true;  
  125.                 }  
  126.             }  
  127.   
  128.             // Finish the handling of the request  
  129.             try {  
  130.                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);  
  131.                 // If we know we are closing the connection, don't drain input.  
  132.                 // This way uploading a 100GB file doesn't tie up the thread   
  133.                 // if the servlet has rejected it.  
  134.                   
  135.                 if(error && !isAsync())  
  136.                     inputBuffer.setSwallowInput(false);  
  137.                 if (!isAsync())  
  138.                     endRequest();  
  139.             } catch (Throwable t) {  
  140.                 ExceptionUtils.handleThrowable(t);  
  141.                 log.error(sm.getString("http11processor.request.finish"), t);  
  142.                 // 500 - Internal Server Error  
  143.                 response.setStatus(500);  
  144.                 adapter.log(request, response, 0);  
  145.                 error = true;  
  146.             }  
  147.             try {  
  148.                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);  
  149.             } catch (Throwable t) {  
  150.                 ExceptionUtils.handleThrowable(t);  
  151.                 log.error(sm.getString("http11processor.response.finish"), t);  
  152.                 error = true;  
  153.             }  
  154.   
  155.             // If there was an error, make sure the request is counted as  
  156.             // and error, and update the statistics counter  
  157.             if (error) {  
  158.                 response.setStatus(500);  
  159.             }  
  160.             request.updateCounters();  
  161.   
  162.             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);  
  163.   
  164.             // Don't reset the param - we'll see it as ended. Next request  
  165.             // will reset it  
  166.             // thrA.setParam(null);  
  167.             // Next request  
  168.             if (!isAsync() || error) {  
  169.                 inputBuffer.nextRequest();  
  170.                 outputBuffer.nextRequest();  
  171.             }  
  172.               
  173.             //hack keep alive behavior  
  174.             break;  
  175.         }  
  176.   
  177.         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);  
  178.         if (error || endpoint.isPaused()) {  
  179.             recycle();  
  180.             return SocketState.CLOSED;  
  181.         } else if (isAsync()) {  
  182.             return SocketState.LONG;  
  183.         } else {  
  184.             if (!keepAlive) {  
  185.                 recycle();  
  186.                 return SocketState.CLOSED;  
  187.             } else {  
  188.                 return SocketState.OPEN;  
  189.             }  
  190.         }   
  191.     }  

这个方法比较长,我们看到在Processor中内置了inputBuffer、outputBuffer和request等对象,其中request和inputBuffer是个双向引用的关系:

[java] view plain copy
print?
  1. request = new Request();  
  2. inputBuffer = new InternalInputBuffer(request, headerBufferSize);  
  3. request.setInputBuffer(inputBuffer);  

这样就可以在处理的时候达到同时处理两个对象的作用,然后在处理过程中可以看到inputBuffer设置了socket的输入流,然后分别调用其parseRequestLine、parseHeaders方法,具体方法内容初略一看就是处理这些输入的字节,有兴趣的可以和Http协议一起做详细的研究,我们这就先不展开了,总之处理完输入流的请求,然后调用prepareRequest方法,由于这部分代码的处理和协议内容关联比较大,如果没有非常熟悉http的数据包结构可能很难直接看源代码读懂,我的方法是断点调试,分别看清楚头尾的request参数有哪些不同,并着重看参数在哪部分代码运行之后起到变化。个人觉得这段代码给request传的值是最明显的:

[java] view plain copy
print?
  1. /** 
  2.      * Parse host. 
  3.      */  
  4.     protected void parseHost(MessageBytes valueMB) {  
  5.   
  6.         if (valueMB == null || valueMB.isNull()) {  
  7.             // HTTP/1.0  
  8.             // Default is what the socket tells us. Overridden if a host is  
  9.             // found/parsed  
  10.             request.setServerPort(socket.getSocket().getLocalPort());  
  11.             InetAddress localAddress = socket.getSocket().getLocalAddress();  
  12.             // Setting the socket-related fields. The adapter doesn't know  
  13.             // about socket.  
  14.             request.serverName().setString(localAddress.getHostName());  
  15.             return;  
  16.         }  

总而言之在给输入流做了处理以后填装进request中,并把request、response全部通过adapter.service处理

[java] view plain copy
print?
  1. /** 
  2.     * Service method. 
  3.     */  
  4.    @Override  
  5.    public void service(org.apache.coyote.Request req,   
  6.                        org.apache.coyote.Response res)  
  7.        throws Exception {  
  8.   
  9.        Request request = (Request) req.getNote(ADAPTER_NOTES);  
  10.        Response response = (Response) res.getNote(ADAPTER_NOTES);  
  11.   
  12.        if (request == null) {  
  13.   
  14.            // Create objects  
  15.            request = connector.createRequest();  
  16.            request.setCoyoteRequest(req);  
  17.            response = connector.createResponse();  
  18.            response.setCoyoteResponse(res);  
  19.   
  20.            // Link objects  
  21.            request.setResponse(response);  
  22.            response.setRequest(request);  
  23.   
  24.            // Set as notes  
  25.            req.setNote(ADAPTER_NOTES, request);  
  26.            res.setNote(ADAPTER_NOTES, response);  
  27.   
  28.            // Set query string encoding  
  29.            req.getParameters().setQueryStringEncoding  
  30.                (connector.getURIEncoding());  
  31.   
  32.        }  
  33.   
  34.        if (connector.getXpoweredBy()) {  
  35.            response.addHeader("X-Powered-By", POWERED_BY);  
  36.        }  
  37.   
  38.        boolean comet = false;  
  39.        boolean async = false;  
  40.          
  41.        try {  
  42.   
  43.            // Parse and set Catalina and configuration specific   
  44.            // request parameters  
  45.            req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());  
  46.            boolean postParseSuccess = postParseRequest(req, request, res, response);  
  47.            if (postParseSuccess) {  
  48.                //check valves if we support async  
  49.                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());  
  50.                // Calling the container  
  51.                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);  
  52.   
  53.                if (request.isComet()) {  
  54.                    if (!response.isClosed() && !response.isError()) {  
  55.                        if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {  
  56.                            // Invoke a read event right away if there are available bytes  
  57.                            if (event(req, res, SocketStatus.OPEN)) {  
  58.                                comet = true;  
  59.                                res.action(ActionCode.COMET_BEGIN, null);  
  60.                            }  
  61.                        } else {  
  62.                            comet = true;  
  63.                            res.action(ActionCode.COMET_BEGIN, null);  
  64.                        }  
  65.                    } else {  
  66.                        // Clear the filter chain, as otherwise it will not be reset elsewhere  
  67.                        // since this is a Comet request  
  68.                        request.setFilterChain(null);  
  69.                    }  
  70.                }  
  71.   
  72.            }  
  73.            AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();  
  74.            if (asyncConImpl != null) {  
  75.                async = true;  
  76.            } else if (!comet) {  
  77.                response.finishResponse();  
  78.                if (postParseSuccess) {  
  79.                    // Log only if processing was invoked.  
  80.                    // If postParseRequest() failed, it has already logged it.  
  81.                    ((Context) request.getMappingData().context).logAccess(  
  82.                            request, response,  
  83.                            System.currentTimeMillis() - req.getStartTime(),  
  84.                            false);  
  85.                }  
  86.                req.action(ActionCode.POST_REQUEST , null);  
  87.            }  
  88.   
  89.        } catch (IOException e) {  
  90.            // Ignore  
  91.        } catch (Throwable t) {  
  92.            ExceptionUtils.handleThrowable(t);  
  93.            log.error(sm.getString("coyoteAdapter.service"), t);  
  94.        } finally {  
  95.            req.getRequestProcessor().setWorkerThreadName(null);  
  96.            // Recycle the wrapper request and response  
  97.            if (!comet && !async) {  
  98.                request.recycle();  
  99.                response.recycle();  
  100.            } else {  
  101.                // Clear converters so that the minimum amount of memory   
  102.                // is used by this processor  
  103.                request.clearEncoders();  
  104.                response.clearEncoders();  
  105.            }  
  106.        }  
  107.   
  108.    }  

所谓adapter,顾名思义就是适配不同的接口,这里我们也看到了,两套不同的request和response在我们眼前展现,第一套是刚才传入的org.apache.coyote包下的Request和Response、第二套就是我们之后要进入下一步操作的org.apache.catalina.connector下的Request和Response,在这段代码中有两行非常重要:
[java] view plain copy
print?
  1. boolean postParseSuccess = postParseRequest(req, request, res, response);  

[java] view plain copy
print?
  1. connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);  

前者是映射内部参数,将上一步把socket输入流中传入的信息解析完成的request交给下一步调用的request处理,后一步则是调用接下来的过程。


我们先看前者,可能有人会要问为啥要那么麻烦开设两套request,其实一开始看代码的时候我也被两套请求类给搞懵了,由于没有对整个架构做过深层次分析,暂时还没有给这个问题准确的答案,但是我们可以提出假想来给后续的研究提供思路,据我而言,这种情况大致可能有两种:

第一种是Tomcat自己设计了一套请求类族,并完成了基本封装功能,但是由于这套类族不符合J2EE设计的类族规范,因此用适配器做适配,这也符合适配器模式本身的意图;

第二种是原本的第一次封装是在一个不分Http和AJP协议的Request中的,我们看Request的全名org.apache.coyote.Request,显然似乎没有做区分,而第二层封装,Request是实现了HttpServletRequest接口的,显然是专门为了Http协议设计的,因此过程中做适配,以达到第一层封装的可复用。


上述两种只是假想,先提出,后续研究来解答


我们继续看postParseRequest方法,由于这个方法比较长,我们截取较为重要的几句:

[java] view plain copy
print?
  1.         String proxyName = connector.getProxyName();  
  2.         int proxyPort = connector.getProxyPort();  
  3.         if (proxyPort != 0) {  
  4.             req.setServerPort(proxyPort);  
  5.         }  
  6.         if (proxyName != null) {  
  7.             req.serverName().setString(proxyName);  
  8.         }  
  9. //...........  
  10.         if (connector.getUseIPVHosts()) {  
  11.             serverName = req.localName();  
  12.             if (serverName.isNull()) {  
  13.                 // well, they did ask for it  
  14.                 res.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, null);  
  15.             }  
  16.         } else {  
  17.             serverName = req.serverName();  
  18.         }  
  19.         //.............  
  20.     connector.getMapper().map(serverName, decodedURI, version,  
  21.                                       request.getMappingData());  
  22.         request.setContext((Context) request.getMappingData().context);  
  23.         request.setWrapper((Wrapper) request.getMappingData().wrapper);  

上述代码可以看到,从第一个request中取出了serverName、url等信息之后转入map方法,然后给第二个request的mappingData做初始化,最后给request设置基本值,这里已经把Context和Wrapper两个容器都设置在了request中,后续将会有大用处。至于map方法调用来调用去的我们具体不说了大家自己看,简单说下就是还记得上一篇讲到容器启动时候初始化完成以后,会有一个mappingListener.init方法给容器进行注册吗?这里就是从注册的容器中选出对应的四个容器并传入request.mappingData。


然后我们再回头这段代码:connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);


前两个调用很好理解,仔细看过上一篇就明白获取到的Container就是engine,而每个容器其实都有一个pipeline,这个pipeline顾名思义是管道的意思,它的命名来源从我阅读的博客来看,是将父子容器的调用关联起来,请求先通过父容器再传输到子容器,其过程就如同管道运输分流一般,而管道中获取valve之后调用invoke方法,由于有四个容器,那么自然有四个valve,我们来看两个,分别hostValve和wrapperValve:

[java] view plain copy
print?
  1. /** 
  2.      * Select the appropriate child Context to process this request, 
  3.      * based on the specified request URI.  If no matching Context can 
  4.      * be found, return an appropriate HTTP error. 
  5.      * 
  6.      * @param request Request to be processed 
  7.      * @param response Response to be produced 
  8.      * 
  9.      * @exception IOException if an input/output error occurred 
  10.      * @exception ServletException if a servlet error occurred 
  11.      */  
  12.     @Override  
  13.     public final void invoke(Request request, Response response)  
  14.         throws IOException, ServletException {  
  15.   
  16.         // Select the Context to be used for this Request  
  17.         Context context = request.getContext();  
  18.         if (context == null) {  
  19.             response.sendError  
  20.                 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,  
  21.                  sm.getString("standardHost.noContext"));  
  22.             return;  
  23.         }  
  24.   
  25.         // Bind the context CL to the current thread  
  26.         if( context.getLoader() != null ) {  
  27.             // Not started - it should check for availability first  
  28.             // This should eventually move to Engine, it's generic.  
  29.             if (Globals.IS_SECURITY_ENABLED) {  
  30.                 PrivilegedAction<Void> pa = new PrivilegedSetTccl(  
  31.                         context.getLoader().getClassLoader());  
  32.                 AccessController.doPrivileged(pa);                  
  33.             } else {  
  34.                 Thread.currentThread().setContextClassLoader  
  35.                         (context.getLoader().getClassLoader());  
  36.             }  
  37.         }  
  38.         if (request.isAsyncSupported()) {  
  39.             request.setAsyncSupported(context.getPipeline().isAsyncSupported());  
  40.         }  
  41.   
  42.   
  43.         // Ask this Context to process this request  
  44.         context.getPipeline().getFirst().invoke(request, response);  
  45.   
  46.         // Access a session (if present) to update last accessed time, based on a  
  47.         // strict interpretation of the specification  
  48.         if (ACCESS_SESSION) {  
  49.             request.getSession(false);  
  50.         }  
  51.   
  52.         // Error page processing  
  53.         response.setSuspended(false);  
  54.   
  55.         Throwable t = (Throwable) request.getAttribute(  
  56.                 RequestDispatcher.ERROR_EXCEPTION);  
  57.   
  58.         if (t != null) {  
  59.             throwable(request, response, t);  
  60.         } else {  
  61.             status(request, response);  
  62.         }  
  63.   
  64.         // Restore the context classloader  
  65.         if (Globals.IS_SECURITY_ENABLED) {  
  66.             PrivilegedAction<Void> pa = new PrivilegedSetTccl(  
  67.                     StandardHostValve.class.getClassLoader());  
  68.             AccessController.doPrivileged(pa);                  
  69.         } else {  
  70.             Thread.currentThread().setContextClassLoader  
  71.                     (StandardHostValve.class.getClassLoader());  
  72.         }  
  73.   
  74.     }  

host中很长一段,但是其核心主要就是context.getPipeline().getFirst().invoke(request, response);,从request中获取context(上述map过程中传入的),并调用子容器valve的invoke,这里也可以明白父子管道的连接是如何实现的。

最终自然调用到了wrapperValve:

[java] view plain copy
print?
  1. /** 
  2.     * Invoke the servlet we are managing, respecting the rules regarding 
  3.     * servlet lifecycle and SingleThreadModel support. 
  4.     * 
  5.     * @param request Request to be processed 
  6.     * @param response Response to be produced 
  7.     * 
  8.     * @exception IOException if an input/output error occurred 
  9.     * @exception ServletException if a servlet error occurred 
  10.     */  
  11.    @Override  
  12.    public final void invoke(Request request, Response response)  
  13.        throws IOException, ServletException {  
  14.   
  15.        // Initialize local variables we may need  
  16.        boolean unavailable = false;  
  17.        Throwable throwable = null;  
  18.        // This should be a Request attribute...  
  19.        long t1=System.currentTimeMillis();  
  20.        requestCount++;  
  21.        StandardWrapper wrapper = (StandardWrapper) getContainer();  
  22.        Servlet servlet = null;  
  23.        Context context = (Context) wrapper.getParent();  
  24.          
  25.        // Check for the application being marked unavailable  
  26.        if (!context.getAvailable()) {  
  27.            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  28.                           sm.getString("standardContext.isUnavailable"));  
  29.            unavailable = true;  
  30.        }  
  31.   
  32.        // Check for the servlet being marked unavailable  
  33.        if (!unavailable && wrapper.isUnavailable()) {  
  34.            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",  
  35.                    wrapper.getName()));  
  36.            long available = wrapper.getAvailable();  
  37.            if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  38.                response.setDateHeader("Retry-After", available);  
  39.                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  40.                        sm.getString("standardWrapper.isUnavailable",  
  41.                                wrapper.getName()));  
  42.            } else if (available == Long.MAX_VALUE) {  
  43.                response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  44.                        sm.getString("standardWrapper.notFound",  
  45.                                wrapper.getName()));  
  46.            }  
  47.            unavailable = true;  
  48.        }  
  49.   
  50.        // Allocate a servlet instance to process this request  
  51.        try {  
  52.            if (!unavailable) {  
  53.                servlet = wrapper.allocate();  
  54.            }  
  55.        } catch (UnavailableException e) {  
  56.            container.getLogger().error(  
  57.                    sm.getString("standardWrapper.allocateException",  
  58.                            wrapper.getName()), e);  
  59.            long available = wrapper.getAvailable();  
  60.            if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  61.                response.setDateHeader("Retry-After", available);  
  62.                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  63.                           sm.getString("standardWrapper.isUnavailable",  
  64.                                        wrapper.getName()));  
  65.            } else if (available == Long.MAX_VALUE) {  
  66.                response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  67.                           sm.getString("standardWrapper.notFound",  
  68.                                        wrapper.getName()));  
  69.            }  
  70.        } catch (ServletException e) {  
  71.            container.getLogger().error(sm.getString("standardWrapper.allocateException",  
  72.                             wrapper.getName()), StandardWrapper.getRootCause(e));  
  73.            throwable = e;  
  74.            exception(request, response, e);  
  75.        } catch (Throwable e) {  
  76.            ExceptionUtils.handleThrowable(e);  
  77.            container.getLogger().error(sm.getString("standardWrapper.allocateException",  
  78.                             wrapper.getName()), e);  
  79.            throwable = e;  
  80.            exception(request, response, e);  
  81.            servlet = null;  
  82.        }  
  83.   
  84.        // Identify if the request is Comet related now that the servlet has been allocated  
  85.        boolean comet = false;  
  86.        if (servlet instanceof CometProcessor   
  87.                && request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) {  
  88.            comet = true;  
  89.            request.setComet(true);  
  90.        }  
  91.          
  92.        // Acknowledge the request  
  93.        try {  
  94.            response.sendAcknowledgement();  
  95.        } catch (IOException e) {  
  96.            container.getLogger().warn(sm.getString("standardWrapper.acknowledgeException",  
  97.                             wrapper.getName()), e);  
  98.            throwable = e;  
  99.            exception(request, response, e);  
  100.        } catch (Throwable e) {  
  101.            ExceptionUtils.handleThrowable(e);  
  102.            container.getLogger().error(sm.getString("standardWrapper.acknowledgeException",  
  103.                             wrapper.getName()), e);  
  104.            throwable = e;  
  105.            exception(request, response, e);  
  106.            servlet = null;  
  107.        }  
  108.        MessageBytes requestPathMB = request.getRequestPathMB();  
  109.        DispatcherType dispatcherType = DispatcherType.REQUEST;  
  110.        if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;   
  111.        request.setAttribute  
  112.            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,  
  113.             dispatcherType);  
  114.        request.setAttribute  
  115.            (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,  
  116.             requestPathMB);  
  117.        // Create the filter chain for this request  
  118.        ApplicationFilterFactory factory =  
  119.            ApplicationFilterFactory.getInstance();  
  120.        ApplicationFilterChain filterChain =  
  121.            factory.createFilterChain(request, wrapper, servlet);  
  122.          
  123.        // Reset comet flag value after creating the filter chain  
  124.        request.setComet(false);  
  125.   
  126.        // Call the filter chain for this request  
  127.        // NOTE: This also calls the servlet's service() method  
  128.        try {  
  129.            if ((servlet != null) && (filterChain != null)) {  
  130.                // Swallow output if needed  
  131.                if (context.getSwallowOutput()) {  
  132.                    try {  
  133.                        SystemLogHandler.startCapture();  
  134.                        if (request.isAsyncDispatching()) {  
  135.                            //TODO SERVLET3 - async  
  136.                            ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();   
  137.                        } else if (comet) {  
  138.                            filterChain.doFilterEvent(request.getEvent());  
  139.                            request.setComet(true);  
  140.                        } else {  
  141.                            filterChain.doFilter(request.getRequest(),   
  142.                                    response.getResponse());  
  143.                        }  
  144.                    } finally {  
  145.                        String log = SystemLogHandler.stopCapture();  
  146.                        if (log != null && log.length() > 0) {  
  147.                            context.getLogger().info(log);  
  148.                        }  
  149.                    }  
  150.                } else {  
  151.                    if (request.isAsyncDispatching()) {  
  152.                        //TODO SERVLET3 - async  
  153.                        ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();  
  154.                    } else if (comet) {  
  155.                        request.setComet(true);  
  156.                        filterChain.doFilterEvent(request.getEvent());  
  157.                    } else {  
  158.                        filterChain.doFilter  
  159.                            (request.getRequest(), response.getResponse());  
  160.                    }  
  161.                }  
  162.   
  163.            }  
  164.        } catch (ClientAbortException e) {  
  165.            throwable = e;  
  166.            exception(request, response, e);  
  167.        } catch (IOException e) {  
  168.            container.getLogger().error(sm.getString(  
  169.                    "standardWrapper.serviceException", wrapper.getName(),  
  170.                    context.getName()), e);  
  171.            throwable = e;  
  172.            exception(request, response, e);  
  173.        } catch (UnavailableException e) {  
  174.            container.getLogger().error(sm.getString(  
  175.                    "standardWrapper.serviceException", wrapper.getName(),  
  176.                    context.getName()), e);  
  177.            //            throwable = e;  
  178.            //            exception(request, response, e);  
  179.            wrapper.unavailable(e);  
  180.            long available = wrapper.getAvailable();  
  181.            if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  182.                response.setDateHeader("Retry-After", available);  
  183.                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  184.                           sm.getString("standardWrapper.isUnavailable",  
  185.                                        wrapper.getName()));  
  186.            } else if (available == Long.MAX_VALUE) {  
  187.                response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  188.                            sm.getString("standardWrapper.notFound",  
  189.                                        wrapper.getName()));  
  190.            }  
  191.            // Do not save exception in 'throwable', because we  
  192.            // do not want to do exception(request, response, e) processing  
  193.        } catch (ServletException e) {  
  194.            Throwable rootCause = StandardWrapper.getRootCause(e);  
  195.            if (!(rootCause instanceof ClientAbortException)) {  
  196.                container.getLogger().error(sm.getString(  
  197.                        "standardWrapper.serviceExceptionRoot",  
  198.                        wrapper.getName(), context.getName(), e.getMessage()),  
  199.                        rootCause);  
  200.            }  
  201.            throwable = e;  
  202.            exception(request, response, e);  
  203.        } catch (Throwable e) {  
  204.            ExceptionUtils.handleThrowable(e);  
  205.            container.getLogger().error(sm.getString(  
  206.                    "standardWrapper.serviceException", wrapper.getName(),  
  207.                    context.getName()), e);  
  208.            throwable = e;  
  209.            exception(request, response, e);  
  210.        }  
  211.   
  212.        // Release the filter chain (if any) for this request  
  213.        if (filterChain != null) {  
  214.            if (request.isComet()) {  
  215.                // If this is a Comet request, then the same chain will be used for the  
  216.                // processing of all subsequent events.  
  217.                filterChain.reuse();  
  218.            } else {  
  219.                filterChain.release();  
  220.            }  
  221.        }  
  222.   
  223.        // Deallocate the allocated servlet instance  
  224.        try {  
  225.            if (servlet != null) {  
  226.                wrapper.deallocate(servlet);  
  227.            }  
  228.        } catch (Throwable e) {  
  229.            ExceptionUtils.handleThrowable(e);  
  230.            container.getLogger().error(sm.getString("standardWrapper.deallocateException",  
  231.                             wrapper.getName()), e);  
  232.            if (throwable == null) {  
  233.                throwable = e;  
  234.                exception(request, response, e);  
  235.            }  
  236.        }  
  237.   
  238.        // If this servlet has been marked permanently unavailable,  
  239.        // unload it and release this instance  
  240.        try {  
  241.            if ((servlet != null) &&  
  242.                (wrapper.getAvailable() == Long.MAX_VALUE)) {  
  243.                wrapper.unload();  
  244.            }  
  245.        } catch (Throwable e) {  
  246.            ExceptionUtils.handleThrowable(e);  
  247.            container.getLogger().error(sm.getString("standardWrapper.unloadException",  
  248.                             wrapper.getName()), e);  
  249.            if (throwable == null) {  
  250.                throwable = e;  
  251.                exception(request, response, e);  
  252.            }  
  253.        }  
  254.        long t2=System.currentTimeMillis();  
  255.   
  256.        long time=t2-t1;  
  257.        processingTime += time;  
  258.        if( time > maxTime) maxTime=time;  
  259.        if( time < minTime) minTime=time;  
  260.   
  261.    }  

我们看到这里用了servlet = wrapper.allocate();步骤来生成servlet,然后调用了servlet的service方法去处理request和Response,至于后续的service转入doPost和doGet是比较好理解的,我们主要来看servlet是如何被生成的:

[java] view plain copy
print?
  1. if (instance == null) {  
  2.                 synchronized (this) {  
  3.                     if (instance == null) {  
  4.                         try {  
  5.                             if (log.isDebugEnabled())  
  6.                                 log.debug("Allocating non-STM instance");  
  7.   
  8.                             instance = loadServlet();  
  9.                             // For non-STM, increment here to prevent a race  
  10.                             // condition with unload. Bug 43683, test case #3  
  11.                             if (!singleThreadModel) {  
  12.                                 newInstance = true;  
  13.                                 countAllocated.incrementAndGet();  
  14.                             }  
  15.                         } catch (ServletException e) {  
  16.                             throw e;  
  17.                         } catch (Throwable e) {  
  18.                             ExceptionUtils.handleThrowable(e);  
  19.                             throw new ServletException  
  20.                                 (sm.getString("standardWrapper.allocate"), e);  
  21.                         }  
  22.                     }  
  23.                 }  
核心代码就是这样,我们看到servlet与其对应的wrapper是单例的,而wrapper是在项目启动时解析web.xml产生的,每个通过配置文件或者注解注册的servlet类只对应了一个wrapper,换句话说servlet的所有类都是单例,两次相同servlet处理的请求其实是由同一个servlet对象进行处理的,只不过运行在不同的线程中而已。因此千万不要再把数据设置为servlet的内部变量然后每次请求去初始化了,因为对于一个非线程安全的数据来说,如果两次同servlet的请求时间差足够短的话,这个数据源可能在第一次请求还没有结束的时候就被第二次请求给初始化了,由此产生的数据bug也是非常可怕的,因此对于请求处理过程的数据都在doPost或doGet方法内再进行定义,切记!


请求的处理过程大体就是这样,刚刚着重讲的是request,其实在这个流程中response应该也明白了,还记得一开始的outputBuffer被填装进入了第一个Response中吗,只要在适配过程中给第二个response传入这个输出流,那么response的print方法就非常容易实现了。


在后续的篇章中我们将继续研究Tomcat的很多细节,如servletContext啊、session啊,以及我们将会和J2EE的规范一起来研究整体的架构设计。









阅读全文
0 0