zoukankan      html  css  js  c++  java
  • Tomcat视角看一次http请求

    一次HTTP请求


    如上图,一次完整的HTT请求,大致包含如下步骤:

    • 域名解析
    • TCP连接(三次握手)
    • 建立连接后发送请求数据
    • 服务器处理请求
    • 发送响应数据
    • 浏览器解析响应数据
    • 浏览器渲染前端页面

    http请求过程看似简单,但如果你对任何事情都抱有一定的好奇心,要从底层根本性的了解以上各个环节,其实及其复杂,需要的知识量也很丰富,基本涵盖了你大学学的所有专业科目(不信?后面我们慢慢来看)。今天我们站着Tomcat的视角,去了解http请求在Tomcat中到底经历了些什么。

    我们将围绕以下步骤进行:Tomcat接收请求、请求数据读取、容器处理业务逻辑、多次请求如何衔接、简单响应等。

     

    底层概念介绍


    1、关于网络

      

      我们所熟知的中间件(mysql、redis、kafka、rabbitmq、dubbo等)其实都是网络上的一个运行的服务,它们从客服端接收数据,通过复杂的逻辑处理后返回客户端。Tomcat当然也不例外,它可以接收来自客户端的多种协议的请求,包括:HTTP、APR等。这里的接收请求和返回请求,自然就涉及到网络编程,在java中网络编程被JDK封装成了Socket接口,我们可以把tomcat理解成一个启动后就一直在运行的Socket服务端程序,它在不停的监听来自外界的请求(其实是TCP请求,是否理解了不管你是http还是其他应用层协议,其实底层都是TCP请求?)。熟悉Socket的同学肯定要问了,那Tomcat是BIO还是NIO呢?没错,只要是网络传输,就逃不掉这个问题。这里先简单说下加,早期的Tomcat是BIO的,具体的版本是7.0(包括)之前都是默认BIO,支持配置成NIO,8.0开始全面默认NIO。当然8.0后的NIO和之前的NIO是有区别的,后续文章我会提到。BIO模型处理流程如下图:

     2、底层数据存储

       数据通过网络传输到Tomcat所在的服务器,首先会在操作系统缓冲区revbuf中,然后tomcat会从revbuf中读取数据,最后存放在自身JVM中。这个步骤看似简单,但是确实现代操作系统的一个经典理论。操作系统和tomcat存放数据都是使用的字节数组,我们仅以Tomcat中字节数据buf为例:

    数据到达Tomcat中后,都会经历以上的字节数组,而后再封装成request对象传递给容器。

    源码分析


    1、Acceptor

     1 /**
     2      * The background thread that listens for incoming TCP/IP connections and
     3      * hands them off to an appropriate processor.
     4      */
     5     /**
     6      * 连接接收类,用于接受来自客户端的所有请求
     7      */
     8     protected class Acceptor extends AbstractEndpoint.Acceptor {
     9 
    10         @Override
    11         public void run() {
    12 
    13             int errorDelay = 0;
    14 
    15             // Loop until we receive a shutdown command
    16             while (running) {
    17 
    18                 // Loop if endpoint is paused
    19                 // 内层循环,暂时中断处理
    20                 while (paused && running) {
    21                     state = AcceptorState.PAUSED;
    22                     try {
    23                         Thread.sleep(50);
    24                     } catch (InterruptedException e) {
    25                         // Ignore
    26                     }
    27                 }
    28 
    29                 //关闭
    30                 if (!running) {
    31                     break;
    32                 }
    33                 state = AcceptorState.RUNNING;
    34 
    35                 try {
    36                     //if we have reached max connections, wait
    37                     // 继承AQS,控制总连接数不能超过最大值
    38                     countUpOrAwaitConnection();
    39 
    40                     Socket socket = null;
    41                     try {
    42                         // Accept the next incoming connection from the server
    43                         // socket
    44                         // 接收socket请求,最重要的入口!!!
    45                         socket = serverSocketFactory.acceptSocket(serverSocket);
    46                     } catch (IOException ioe) {
    47                         countDownConnection();
    48                         // Introduce delay if necessary
    49                         errorDelay = handleExceptionWithDelay(errorDelay);
    50                         // re-throw
    51                         throw ioe;
    52                     }
    53                     // Successful accept, reset the error delay
    54                     errorDelay = 0;
    55 
    56                     // Configure the socket
    57                     if (running && !paused && setSocketOptions(socket)) {
    58                         // Hand this socket off to an appropriate processor
    59                         if (!processSocket(socket)) {
    60                             countDownConnection();
    61                             // Close socket right away
    62                             closeSocket(socket);
    63                         }
    64                     } else {
    65                         countDownConnection();
    66                         // Close socket right away
    67                         closeSocket(socket);
    68                     }
    69                 } catch (IOException x) {
    70                     if (running) {
    71                         log.error(sm.getString("endpoint.accept.fail"), x);
    72                     }
    73                 } catch (NullPointerException npe) {
    74                     if (running) {
    75                         log.error(sm.getString("endpoint.accept.fail"), npe);
    76                     }
    77                 } catch (Throwable t) {
    78                     ExceptionUtils.handleThrowable(t);
    79                     log.error(sm.getString("endpoint.accept.fail"), t);
    80                 }
    81             }
    82             state = AcceptorState.ENDED;
    83         }
    84     }

     Acceptor继承了AbstractEndpoint类的内部类Acceptor,这个Acceptor实现了Runnable接口,表明自己是一个多线程的任务类,可放入线程池。Tomcat启动后,外层Acceptor就开始执行run方法。

    11行,实现了Runnable接口中的run方法,可以看见该run方法中是一个while循环,只有当running=false,循环才跳出。

    45行,这里是关键,tomcat启动后,如果没有请求进来,当前主线程将在此处进行阻塞。直到第一个请求到来,便获取当前请求的socket。

    59行,将得到的socekt交由processSocket()方法处理。

    其他的代码都是一些异常情况的判断,这里忽略。

    2、processSocket

     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             wrapper.setSecure(isSSLEnabled());
     7             // During shutdown, executor may be null - avoid NPE
     8             if (!running) {
     9                 return false;
    10             }
    11             // 将包装好的socketProcessor扔线程池
    12             getExecutor().execute(new SocketProcessor(wrapper));
    13         } catch (RejectedExecutionException x) {
    14             log.warn("Socket processing request was rejected for:"+socket,x);
    15             return false;
    16         } catch (Throwable t) {
    17             ExceptionUtils.handleThrowable(t);
    18             // This means we got an OOM or similar creating a thread, or that
    19             // the pool and its queue are full
    20             log.error(sm.getString("endpoint.process.fail"), t);
    21             return false;
    22         }
    23         return true;
    24     }

    process方法将socket包装成SocketWrapper,再设置为SocketProcessor的属性

    12行,SocketProcessor类实现了Runnable接口,这里相当于将当前请求的socket扔到了线程池当中。

    3、SocketProcessor

     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==null) throw 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                         // 这里处理HTTPS相关的逻辑
    30                         serverSocketFactory.handshake(socket.getSocket());
    31                     } catch (Throwable t) {
    32                         ExceptionUtils.handleThrowable(t);
    33                         if (log.isDebugEnabled()) {
    34                             log.debug(sm.getString("endpoint.err.handshake"), t);
    35                         }
    36                         // Tell to close the socket
    37                         state = SocketState.CLOSED;
    38                     }
    39 
    40                     /**
    41                      * handler处理socket
    42                      */
    43                     if ((state != SocketState.CLOSED)) {
    44                         if (status == null) {
    45                             state = handler.process(socket, SocketStatus.OPEN_READ);
    46                         } else {
    47                             state = handler.process(socket,status);
    48                         }
    49                     }
    50                     if (state == SocketState.CLOSED) {
    51                         // Close socket
    52                         if (log.isTraceEnabled()) {
    53                             log.trace("Closing socket:"+socket);
    54                         }
    55                         countDownConnection();
    56                         try {
    57                             socket.getSocket().close();
    58                         } catch (IOException e) {
    59                             // Ignore
    60                         }
    61                     } else if (state == SocketState.OPEN ||
    62                             state == SocketState.UPGRADING ||
    63                             state == SocketState.UPGRADING_TOMCAT  ||
    64                             state == SocketState.UPGRADED){
    65                         socket.setKeptAlive(true);
    66                         socket.access();
    67                         launch = true;
    68                     } else if (state == SocketState.LONG) {
    69                         socket.access();
    70                         waitingRequests.add(socket);
    71                     }
    72                 } finally {
    73                     if (launch) {
    74                         try {
    75                             getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
    76                         } catch (RejectedExecutionException x) {
    77                             log.warn("Socket reprocessing request was rejected for:"+socket,x);
    78                             try {
    79                                 //unable to handle connection at this time
    80                                 handler.process(socket, SocketStatus.DISCONNECT);
    81                             } finally {
    82                                 countDownConnection();
    83                             }
    84 
    85 
    86                         } catch (NullPointerException npe) {
    87                             if (running) {
    88                                 log.error(sm.getString("endpoint.launch.fail"),
    89                                         npe);
    90                             }
    91                         }
    92                     }
    93                 }
    94             }
    95             socket = null;
    96             // Finish up this request
    97         }
    98 
    99     }

    这个类主要是处理买个socket连接中接收到的http请求

    45行,将socket交由handler处理,其他的都是异常情况处理

    4、handler

      1 public SocketState process(SocketWrapper<S> wrapper,
      2                 SocketStatus status) {
      3              23 
     24             try {
     25                 if (processor == null) {
     26                     processor = recycledProcessors.poll();
     27                 }
     28                 if (processor == null) {
     29                     processor = createProcessor();
     30                 }
     31 
     32                 initSsl(wrapper, processor);
     33 
     34                 SocketState state = SocketState.CLOSED;
     35                 do {
     36                     if (status == SocketStatus.DISCONNECT &&
     37                             !processor.isComet()) {
     38                         // Do nothing here, just wait for it to get recycled
     39                         // Don't do this for Comet we need to generate an end
     40                         // event (see BZ 54022)
     41                     } else if (processor.isAsync() || state == SocketState.ASYNC_END) {
     42                         state = processor.asyncDispatch(status);
     43                         if (state == SocketState.OPEN) {
     44                             // release() won't get called so in case this request
     45                             // takes a long time to process, remove the socket from
     46                             // the waiting requests now else the async timeout will
     47                             // fire
     48                             getProtocol().endpoint.removeWaitingRequest(wrapper);
     49                             // There may be pipe-lined data to read. If the data
     50                             // isn't processed now, execution will exit this
     51                             // loop and call release() which will recycle the
     52                             // processor (and input buffer) deleting any
     53                             // pipe-lined data. To avoid this, process it now.
     54                             state = processor.process(wrapper);
     55                         }
     56                     } else if (processor.isComet()) {
     57                         state = processor.event(status);
     58                     } else if (processor.getUpgradeInbound() != null) {
     59                         state = processor.upgradeDispatch();
     60                     } else if (processor.isUpgrade()) {
     61                         state = processor.upgradeDispatch(status);
     62                     } else {
     63                         // 处理socket
     64                         state = processor.process(wrapper);
     65                     }
     66 
     67                     if (processor.isAsync()) {
     68                         state = processor.asyncPostProcess();
     69                     }
     70 
     71                     if (state == SocketState.UPGRADING) {
     72                         // Get the HTTP upgrade handler
     73                         HttpUpgradeHandler httpUpgradeHandler =
     74                                 processor.getHttpUpgradeHandler();
     75                         // Release the Http11 processor to be re-used
     76                         release(wrapper, processor, false, false);
     77                         // Create the upgrade processor
     78                         processor = createUpgradeProcessor(
     79                                 wrapper, httpUpgradeHandler);
     80                         // Mark the connection as upgraded
     81                         wrapper.setUpgraded(true);
     82                         // Associate with the processor with the connection
     83                         connections.put(socket, processor);
     84                         // Initialise the upgrade handler (which may trigger
     85                         // some IO using the new protocol which is why the lines
     86                         // above are necessary)
     87                         // This cast should be safe. If it fails the error
     88                         // handling for the surrounding try/catch will deal with
     89                         // it.
     90                         httpUpgradeHandler.init((WebConnection) processor);
     91                     } else if (state == SocketState.UPGRADING_TOMCAT) {
     92                         // Get the UpgradeInbound handler
     93                         org.apache.coyote.http11.upgrade.UpgradeInbound inbound =
     94                                 processor.getUpgradeInbound();
     95                         // Release the Http11 processor to be re-used
     96                         release(wrapper, processor, false, false);
     97                         // Create the light-weight upgrade processor
     98                         processor = createUpgradeProcessor(wrapper, inbound);
     99                         inbound.onUpgradeComplete();
    100                     }
    101                     if (getLog().isDebugEnabled()) {
    102                         getLog().debug("Socket: [" + wrapper +
    103                                 "], Status in: [" + status +
    104                                 "], State out: [" + state + "]");
    105                     }
    106                 } while (state == SocketState.ASYNC_END ||
    107                         state == SocketState.UPGRADING ||
    108                         state == SocketState.UPGRADING_TOMCAT);
    109 
    110                 if (state == SocketState.LONG) {
    111                     // In the middle of processing a request/response. Keep the
    112                     // socket associated with the processor. Exact requirements
    113                     // depend on type of long poll
    114                     connections.put(socket, processor);
    115                     longPoll(wrapper, processor);
    116                 } else if (state == SocketState.OPEN) {
    117                     // In keep-alive but between requests. OK to recycle
    118                     // processor. Continue to poll for the next request.
    119                     connections.remove(socket);
    120                     release(wrapper, processor, false, true);
    121                 } else if (state == SocketState.SENDFILE) {
    122                     // Sendfile in progress. If it fails, the socket will be
    123                     // closed. If it works, the socket either be added to the
    124                     // poller (or equivalent) to await more data or processed
    125                     // if there are any pipe-lined requests remaining.
    126                     connections.put(socket, processor);
    127                 } else if (state == SocketState.UPGRADED) {
    128                     // Need to keep the connection associated with the processor
    129                     connections.put(socket, processor);
    130                     // Don't add sockets back to the poller if this was a
    131                     // non-blocking write otherwise the poller may trigger
    132                     // multiple read events which may lead to thread starvation
    133                     // in the connector. The write() method will add this socket
    134                     // to the poller if necessary.
    135                     if (status != SocketStatus.OPEN_WRITE) {
    136                         longPoll(wrapper, processor);
    137                     }
    138                 } else {
    139                     // Connection closed. OK to recycle the processor. Upgrade
    140                     // processors are not re-used but recycle is called to clear
    141                     // references.
    142                     connections.remove(socket);
    143                     if (processor.isUpgrade()) {
    144                         processor.getHttpUpgradeHandler().destroy();
    145                         processor.recycle(true);
    146                     } else if (processor instanceof org.apache.coyote.http11.upgrade.UpgradeProcessor) {
    147                         // NO-OP
    148                     } else {
    149                         release(wrapper, processor, true, false);
    150                     }
    151                 }
    152                 return state;
    153             } catch(java.net.SocketException e) {
    154                 // SocketExceptions are normal
    155                 getLog().debug(sm.getString(
    156                         "abstractConnectionHandler.socketexception.debug"), e);
    157             } catch (java.io.IOException e) {
    158                 // IOExceptions are normal
    159                 getLog().debug(sm.getString(
    160                         "abstractConnectionHandler.ioexception.debug"), e);
    161             }
    162             // Future developers: if you discover any other
    163             // rare-but-nonfatal exceptions, catch them here, and log as
    164             // above.
    165             catch (OutOfMemoryError oome) {
    166                 // Try and handle this here to give Tomcat a chance to close the
    167                 // connection and prevent clients waiting until they time out.
    168                 // Worst case, it isn't recoverable and the attempt at logging
    169                 // will trigger another OOME.
    170                 getLog().error(sm.getString("abstractConnectionHandler.oome"), oome);
    171             } catch (Throwable e) {
    172                 ExceptionUtils.handleThrowable(e);
    173                 // any other exception or error is odd. Here we log it
    174                 // with "ERROR" level, so it will show up even on
    175                 // less-than-verbose logs.
    176                 getLog().error(
    177                         sm.getString("abstractConnectionHandler.error"), e);
    178             }
    179             // Make sure socket/processor is removed from the list of current
    180             // connections
    181             connections.remove(socket);
    182             // Don't try to add upgrade processors back into the pool
    183             if (!(processor instanceof org.apache.coyote.http11.upgrade.UpgradeProcessor)
    184                     && !processor.isUpgrade()) {
    185                 release(wrapper, processor, true, false);
    186             }
    187             return SocketState.CLOSED;
    188         }

    68行,继续将socket往下传

      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         setSocketWrapper(socketWrapper);
      8         /**
      9          * 设置socket的InputStream和OutStream,供后面读取数据和响应使用
     10          */
     11         getInputBuffer().init(socketWrapper, endpoint);
     12         getOutputBuffer().init(socketWrapper, endpoint);
     13 
     14         // Flags
     15         keepAlive = true;
     16         comet = false;
     17         openSocket = false;
     18         sendfileInProgress = false;
     19         readComplete = true;
     20         if (endpoint.getUsePolling()) {
     21             keptAlive = false;
     22         } else {
     23             keptAlive = socketWrapper.isKeptAlive();
     24         }
     25 
     26         /**
     27          * 长连接相关,判断当前socket是否继续处理接下来的请求
     28          */
     29         if (disableKeepAlive()) {
     30             socketWrapper.setKeepAliveLeft(0);
     31         }
     32 
     33         /**
     34          * 处理socket中的请求,在长连接的模式下,会一直执行当前循环
     35          */
     36         while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
     37                 upgradeInbound == null &&
     38                 httpUpgradeHandler == null && !endpoint.isPaused()) {
     39 
     40             // Parsing the request header
     41             try {
     42                 /**
     43                  * 1、设置socket超时时间
     44                  * 2、第一次从socket中读取数据
     45                  */
     46                 setRequestLineReadTimeout();
     47 
     48                 // 读取HTTP请求行数据
     49                 if (!getInputBuffer().parseRequestLine(keptAlive)) {
     50                     if (handleIncompleteRequestLineRead()) {
     51                         break;
     52                     }
     53                 }
     54 
     55                 // Process the Protocol component of the request line
     56                 // Need to know if this is an HTTP 0.9 request before trying to
     57                 // parse headers.
     58                 prepareRequestProtocol();
     59 
     60                 if (endpoint.isPaused()) {
     61                     // 503 - Service unavailable
     62                     response.setStatus(503);
     63                     setErrorState(ErrorState.CLOSE_CLEAN, null);
     64                 } else {
     65                     keptAlive = true;
     66                     // Set this every time in case limit has been changed via JMX
     67                     // 设置请求行和请求头大小,注意,这个很重要!
     68                     request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
     69                     // 设置做多可设置cookie数量
     70                     request.getCookies().setLimit(getMaxCookieCount());
     71                     // Currently only NIO will ever return false here
     72                     // Don't parse headers for HTTP/0.9
     73                     if (!http09 && !getInputBuffer().parseHeaders()) {
     74                         // We've read part of the request, don't recycle it
     75                         // instead associate it with the socket
     76                         openSocket = true;
     77                         readComplete = false;
     78                         break;
     79                     }
     80                     if (!disableUploadTimeout) {
     81                         setSocketTimeout(connectionUploadTimeout);
     82                     }
     83                 }
     84             } catch (IOException e) {
     85                 if (getLog().isDebugEnabled()) {
     86                     getLog().debug(
     87                             sm.getString("http11processor.header.parse"), e);
     88                 }
     89                 setErrorState(ErrorState.CLOSE_NOW, e);
     90                 break;
     91             } catch (Throwable t) {
     92                 ExceptionUtils.handleThrowable(t);
     93                 UserDataHelper.Mode logMode = userDataHelper.getNextMode();
     94                 if (logMode != null) {
     95                     String message = sm.getString(
     96                             "http11processor.header.parse");
     97                     switch (logMode) {
     98                         case INFO_THEN_DEBUG:
     99                             message += sm.getString(
    100                                     "http11processor.fallToDebug");
    101                             //$FALL-THROUGH$
    102                         case INFO:
    103                             getLog().info(message, t);
    104                             break;
    105                         case DEBUG:
    106                             getLog().debug(message, t);
    107                     }
    108                 }
    109                 // 400 - Bad Request
    110                 response.setStatus(400);
    111                 setErrorState(ErrorState.CLOSE_CLEAN, t);
    112                 getAdapter().log(request, response, 0);
    113             }
    114 
    115             if (!getErrorState().isError()) {
    116                 // Setting up filters, and parse some request headers
    117                 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
    118                 try {
    119                     prepareRequest();
    120                 } catch (Throwable t) {
    121                     ExceptionUtils.handleThrowable(t);
    122                     if (getLog().isDebugEnabled()) {
    123                         getLog().debug(sm.getString(
    124                                 "http11processor.request.prepare"), t);
    125                     }
    126                     // 500 - Internal Server Error
    127                     response.setStatus(500);
    128                     setErrorState(ErrorState.CLOSE_CLEAN, t);
    129                     getAdapter().log(request, response, 0);
    130                 }
    131             }
    132 
    133             if (maxKeepAliveRequests == 1) {
    134                 keepAlive = false;
    135             } else if (maxKeepAliveRequests > 0 &&
    136                     socketWrapper.decrementKeepAlive() <= 0) {
    137                 keepAlive = false;
    138             }
    139 
    140             // Process the request in the adapter
    141             if (!getErrorState().isError()) {
    142                 try {
    143                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
    144                     /**
    145                      * 将封装好的请求和响应对象,交由容器处理
    146                      * service-->host-->context-->wrapper-->servlet
    147                      * 这里非常重要,我们所写的servlet代码正是这里在调用,它遵循了Servlet规范
    148                      * 这里处理完,代表程序员开发的servlet已经执行完毕
    149                      */
    150                     adapter.service(request, response);
    151                     // Handle when the response was committed before a serious
    152                     // error occurred.  Throwing a ServletException should both
    153                     // set the status to 500 and set the errorException.
    154                     // If we fail here, then the response is likely already
    155                     // committed, so we can't try and set headers.
    156                     if(keepAlive && !getErrorState().isError() && (
    157                             response.getErrorException() != null ||
    158                                     (!isAsync() &&
    159                                     statusDropsConnection(response.getStatus())))) {
    160                         setErrorState(ErrorState.CLOSE_CLEAN, null);
    161                     }
    162                     setCometTimeouts(socketWrapper);
    163                 } catch (InterruptedIOException e) {
    164                     setErrorState(ErrorState.CLOSE_NOW, e);
    165                 } catch (HeadersTooLargeException e) {
    166                     getLog().error(sm.getString("http11processor.request.process"), e);
    167                     // The response should not have been committed but check it
    168                     // anyway to be safe
    169                     if (response.isCommitted()) {
    170                         setErrorState(ErrorState.CLOSE_NOW, e);
    171                     } else {
    172                         response.reset();
    173                         response.setStatus(500);
    174                         setErrorState(ErrorState.CLOSE_CLEAN, e);
    175                         response.setHeader("Connection", "close"); // TODO: Remove
    176                     }
    177                 } catch (Throwable t) {
    178                     ExceptionUtils.handleThrowable(t);
    179                     getLog().error(sm.getString("http11processor.request.process"), t);
    180                     // 500 - Internal Server Error
    181                     response.setStatus(500);
    182                     setErrorState(ErrorState.CLOSE_CLEAN, t);
    183                     getAdapter().log(request, response, 0);
    184                 }
    185             }
    186 
    187             // Finish the handling of the request
    188             rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
    189 
    190             if (!isAsync() && !comet) {
    191                 if (getErrorState().isError()) {
    192                     // If we know we are closing the connection, don't drain
    193                     // input. This way uploading a 100GB file doesn't tie up the
    194                     // thread if the servlet has rejected it.
    195                     getInputBuffer().setSwallowInput(false);
    196                 } else {
    197                     // Need to check this again here in case the response was
    198                     // committed before the error that requires the connection
    199                     // to be closed occurred.
    200                     checkExpectationAndResponseStatus();
    201                 }
    202                 /**
    203                  * 当前请求收尾工作
    204                  * 判断请求体是否读取完毕,没有则读取完毕,并修正pos
    205                  */
    206                 endRequest();
    207             }
    208 
    209             rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
    210 
    211             // If there was an error, make sure the request is counted as
    212             // and error, and update the statistics counter
    213             if (getErrorState().isError()) {
    214                 response.setStatus(500);
    215             }
    216             request.updateCounters();
    217 
    218             if (!isAsync() && !comet || getErrorState().isError()) {
    219                 if (getErrorState().isIoAllowed()) {
    220                     /**
    221                      * 根据修正完的pos和lastValid,初始化数组下标,以便继续处理下一次请求
    222                      */
    223                     getInputBuffer().nextRequest();
    224                     getOutputBuffer().nextRequest();
    225                 }
    226             }
    227 
    228             if (!disableUploadTimeout) {
    229                 if(endpoint.getSoTimeout() > 0) {
    230                     setSocketTimeout(endpoint.getSoTimeout());
    231                 } else {
    232                     setSocketTimeout(0);
    233                 }
    234             }
    235 
    236             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
    237 
    238             if (breakKeepAliveLoop(socketWrapper)) {
    239                 break;
    240             }
    241         }
    242 
    243         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
    244 
    245         if (getErrorState().isError() || endpoint.isPaused()) {
    246             return SocketState.CLOSED;
    247         } else if (isAsync() || comet) {
    248             return SocketState.LONG;
    249         } else if (isUpgrade()) {
    250             return SocketState.UPGRADING;
    251         } else if (getUpgradeInbound() != null) {
    252             return SocketState.UPGRADING_TOMCAT;
    253         } else {
    254             if (sendfileInProgress) {
    255                 return SocketState.SENDFILE;
    256             } else {
    257                 if (openSocket) {
    258                     if (readComplete) {
    259                         return SocketState.OPEN;
    260                     } else {
    261                         return SocketState.LONG;
    262                     }
    263                 } else {
    264                     return SocketState.CLOSED;
    265                 }
    266             }
    267         }
    268     }

    这个方法是关键中的关键,几乎大部分的逻辑都在这里

    11-12行,获取socket的InputStream和OutputStream,这2个对象分别是从操作系统缓冲区中读取数据以及写入数据到缓冲区。

    1 @Override
    2     protected void init(SocketWrapper<Socket> socketWrapper,
    3             AbstractEndpoint<Socket> endpoint) throws IOException {
    4         inputStream = socketWrapper.getSocket().getInputStream();
    5     }

    46行,设置socket超时时间,以及第一次读取socket中数据,这里可能只是读取部分数据,后面会多次读取。

    49行,读取HTTP请求行数据

    73行,读取请求头数据

    150行,将封装好的request和response对象传给容器,最终给到servlet处理业务逻辑,这行执行完成,表明程序员开发的业务逻辑已经执行完毕

    206行,当前请求收尾:如果servlet中没有读取获取只读取部分请求体,这里读取完毕,并修正下面pos值

    223行,根据pos和lastValid值,初始化buf数组下下标:lastValid=lastValid-pos;pos=0

    这几个步骤处理比较复杂,主要有以下几点:

    1、while循环用于保证长连接情况下一个socket能处理多个HTTP请求。

    2、buf数组用户存放读取自socket的所有请求数据,包括:HTTP请求行,请求头,以及请求体。演变过程如下

      初始化大小为8KB的字节数组buf,下标pos=0,lastValid=0

     每次都是从socket中读取一定长度的数据到buf数组,注意:一次读取的数据没有任何规律可言,可能只有请求行,也可能包括请求行和请求头,甚至还有可能包括请求体。

         

    一个重点:HTTP请求GET方法,请求入参大小有限制。我们很多人大概都知道这个机制,可是并不知道,为啥又限制,以及限制到底是多大,其实Tomcat在读取请求数据的时候,分了两种情况

      1、请求行和请求头

    Tomcat读取请求行和请求头后,最终数组会变成

    当请求头读完后,数组会包含两部分,如果读取的过程中,lastValid==buf.length,这时会抛出异常:iib.requestheadertoolarge.error!原来如此,其实Tomcat是想请求行和请求头一起打包放在数组中,只要大小不超过8KB,都OK,如果超过就抛异常,现在明白了啊,并不是GET方法请求入参大小有限制,当然,这里都是默认的大小,程序员可以自行修改配置。

      2、读取请求体

    上面演示了读取请求行和请求头的,它们只在一个buf数组中完成。而请求体不一样,我们知道,请求体的数据大小可以是任意的,因此Tomcat不能做任何限制

    如果buf中剩余大小>4500,Tomcat每次回从socket中读取大小为buf.length-end的数据放入buf中,pos=end,lastValid=pos+实际读取数据的长度。注意这里每次存放的位置都是从end开始,可以看出请求体读取的时候是采用覆盖的方式,即上次读取的数据下个循环在读取的时候就被覆盖了。因此也可以看出请求体的读取如果业务代码不做保存,那就没法再次读取的,切记!

    当buf数组中剩余大小不足4500时,Tomcat便会重新初始化一个新的数组赋给变量buf(原先的数组由于没有了引用,交由JVM垃圾回收),在新的buf中完成请求体的读取,具体逻辑同上。

    下面我们来欣赏一下数据读取的代码,感受下作者高深的编程思想

     1 protected boolean fill(boolean block) throws IOException {
     2 
     3         int nRead = 0;
     4 
     5         /**
     6          * 这个核心就是读取socket中数据到缓冲区buf中,循环读取,2种情况
     7          * 1、请求行和请求头:不能超过缓冲区大小(默认8kb),如果超过,则抛异常,读完后将parsingHeader设置为false
     8          * 2、请求行:没有任何大小限制,循环读取,如果剩下的少于4500个字节,则会重新创建buf数组,从头开始读取,直到读完位置,注意!buf原先引用的数组们,等待GC
     9          */
    10         if (parsingHeader) {
    11 
    12             /**
    13              * 从socket中读取数据大于tomcat中缓冲区buf的长度,直接抛异常,这里有两点
    14              * 1、这个就是我们很多时候很多人说的,get请求url不能过长的原因,其实是header和url等总大小不能超过8kb
    15              * 2、这里的buf非常总要,它是InternalInputBuffer的属性,是一个字节数据,用户暂存从socket中读取的数据,比如:请求行,请求头、请求体
    16              */
    17             if (lastValid == buf.length) {
    18                 throw new IllegalArgumentException
    19                     (sm.getString("iib.requestheadertoolarge.error"));
    20             }
    21 
    22             // 将socket中的数据读到缓冲区buf中,注意!这里就是BIO之所以难懂的关键所在,它会阻塞!
    23             // 这个方法会阻塞,如果没有数据可读,则会一直阻塞,有数据,则移动lastValid位置
    24             nRead = inputStream.read(buf, pos, buf.length - lastValid);
    25             if (nRead > 0) {
    26                 lastValid = pos + nRead;
    27             }
    28 
    29         } else {
    30 
    31             if (buf.length - end < 4500) {
    32                 // In this case, the request header was really large, so we allocate a
    33                 // brand new one; the old one will get GCed when subsequent requests
    34                 // clear all references
    35                 buf = new byte[buf.length];
    36                 end = 0;
    37             }
    38             pos = end;
    39             lastValid = pos;
    40             nRead = inputStream.read(buf, pos, buf.length - lastValid);
    41             if (nRead > 0) {
    42                 lastValid = pos + nRead;
    43             }
    44 
    45         }
    46         // 原则上这个方法要么阻塞着,要么就返回true
    47         return (nRead > 0);
    48 
    49     }

      3、请求收尾

    如果请求是长连接,那么一次HTTP处理完成后,socket通道并未关闭,那么怎么样再次处理下个请求发送的数据呢?

    我们可以想象,如果是长连接的,也就是客户端在发送上一个请求的请求体后,立马又发送了下一个请求的请求行。所以分两组情况:

    1、最后一次读取请求体,刚好读完

    pos=lastValid=0

      2、最后一次读取请求体,顺便读出了部分下一次请求的数据

     可以看出:首先要讲pos修正为请求体实际截止的位置,然后再初始化下标:lastValid=lastValid-pos,pos=0。

    到这里一次请求几把已经结束,如果是长连接,便会再次进去while循环,继续读取客户端发送的数据;如果非长连接,则就关闭socket链接,请求结束!

    总结


    我们从Tomcat角度深入了解了HTTP请求的处理过程,其中涉及网络、操作系统、内存等知识。经过这次学习,我对以下几点有了更深的理解

    1、何为应用层协议,HTTP请求的本质其实也是TCP传输

    2、长连接的本质

    3、Tomcat底层数据存储模式

    4、HTTP请求是如果到达servlet的,这是很多MVC框架的基石

    5、Tomcat作者过人的设计思想和高深的编程功底

  • 相关阅读:
    react-redux: counter
    react table dropdown
    react pagination
    react privateRoute
    分布式爬虫(2)
    CentOS安装Python3.x
    spark复习笔记(3):使用spark实现单词统计
    《快学scala》读书笔记(2)
    《快学scala》读书笔记(1)
    spark复习笔记(2)
  • 原文地址:https://www.cnblogs.com/runnable/p/12905401.html
Copyright © 2011-2022 走看看