Tomcat7.0源码分析——请求原理分析(中)

前言

  在《Tomcat7.0源码分析——请求原理分析(上)》一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握《Tomcat7.0源码分析——请求原理分析(上)》一文中的相关知识以及HTTP协议和TCP协议的一些内容。本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析。

请求处理架构

  在正式开始之前,我们先来看看图1中的Tomcat请求处理架构。

Tomcat7.0源码分析——请求原理分析(中)

图1  Tomcat请求处理架构

图1列出了Tomcat请求处理架构中的主要组件,这里对它们做个简单介绍:

  • Acceptor:负责从ServerSocket中接收新的连接,并将Socket转交给SocketProcessor处理。Acceptor是JIoEndpoint的内部类,其实现已在《Tomcat7.0源码分析——请求原理分析(上)》一文中介绍。Acceptor线程的默认数量为1,我们可以在server.xml的Connector配置中增加acceptorThreadCount的大小。
  • SocketProcessor:负责对Acceptor转交的Socket进行处理,包括给Socket设置属性、读取请求行和请求头等,最终将处理交给Engine的Pipeline处理。
  • ThreadPool:执行SocketProcessor的线程来自《Tomcat7.0源码分析——请求原理分析(上)》一文中介绍的线程池,此线程池默认的最小线程数minSpareThreads等于10,最大线程数maxThreads等于200,我们可以在server.xml的Connector配置中调整它们的大小。
  • Pipeline:SocketProcessor线程最后会将请求进一步交给Engine容器的Pipeline,管道Pipeline包括一系列的valve,如:StandardEngineValve、AccessLogValve、ErrorReportValve、StandardHostValve、 StandardContextValve、 StandardWrapperValve,它们就像地下水管中的一个个阀门,每一个都会对请求数据做不同的处理。
  • FilterChain:管道Pipeline的最后一个valve是StandardWrapperValve,它会负责生成Servlet和Filter实例,并将它们组织成对请求处理的链条,这里正是Tomcat与J2EE规范相结合的部分。
   默认情况下,Tomcat只有一个Acceptor线程,Acceptor不断循环从ServerSocket中获取Socket,当并发数大的情况下,这里会不会有性能问题?我想说的是,Acceptor的实现非常轻量级,它只负责两个动作:获取Socket和将Socket转交给SocketProcessor线程处理。另外,我们可以通过在server.xml的Connector配置中增加acceptorThreadCount的值,让我们同时可以拥有多个Acceptor线程。虽然我们可以修改maxThreads配置把SocketProcessor的线程数设置的很大,但是我们需要区别对待:

  • 如果你部署在Tomcat上的Web服务主要用于计算,那么CPU的开销势必会很大,那么线程数不宜设置的过大,一般以CPU核数*2——CPU核数*3最佳。当然如果计算量非常大,就已经超出了Tomcat的使用范畴,我想此时,选择离线计算框架Hadoop或者实时计算框架Storm、Spark才是更好的选择。
  • 如果部署在Tomcat上的Web服务主要是为了提供数据库访问,此时I/O的开销会很大,而CPU利用率反而低,此时应该将线程数设置的大一些,但是如果设置的过大,CPU为了给成百上千个线程分配时间片,造成CPU的精力都分散在线程切换上,反而造成性能下降。具体多大,需要对系统性能调优得出。
   原理就讲这么多,下面具体分析下Tomcat处理请求的具体实现。

接收请求

  在《Tomcat7.0源码分析——请求原理分析(上)》一文中我们曾经介绍过JIoEndpoint的内部类Acceptor,Acceptor实现了Runnable接口。Acceptor作为后台线程不断循环,每次循环都会接收来自浏览器的Socket连接(用户在浏览器输入HTTP请求地址后,浏览器底层实际使用Socket通信的),最后将Socket交给外部类JIoEndpoint的processSocket方法(见代码清单1)处理。

代码清单1

[java] view plain copy
  1. /** 
  2.  * Process given socket. 
  3.  */  
  4. protected boolean processSocket(Socket socket) {  
  5.     try {  
  6.         SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  
  7.         wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());  
  8.         getExecutor().execute(new SocketProcessor(wrapper));  
  9.     } catch (RejectedExecutionException x) {  
  10.         log.warn("Socket processing request was rejected for:"+socket,x);  
  11.         return false;  
  12.     } catch (Throwable t) {  
  13.         // This means we got an OOM or similar creating a thread, or that  
  14.         // the pool and its queue are full  
  15.         log.error(sm.getString("endpoint.process.fail"), t);  
  16.         return false;  
  17.     }  
  18.     return true;  
  19. }  

根据代码清单1,JIoEndpoint的processSocket方法的处理步骤如下:

  1. 将Socket封装为SocketWrapper;
  2. 给SocketWrapper设置连接保持时间keepAliveLeft。这个值是通过调用父类AbstractEndpoint的getMaxKeepAliveRequests方法(见代码清单2)获得的;
  3. 创建SocketProcessor(此类也是JIoEndpoint的内部类,而且也实现了Runnable接口,见代码清单3),并使用线程池(此线程池已在《Tomcat7.0源码分析——请求原理分析(上)》一文中启动PROTOCOLHANDLER一节介绍)执行。
代码清单2
[java] view plain copy
  1. /** 
  2.  * Max keep alive requests  
  3.  */  
  4. private int maxKeepAliveRequests=100// as in Apache HTTPD server  
  5. public int getMaxKeepAliveRequests() {  
  6.     return maxKeepAliveRequests;  
  7. }  

代码清单3

[java] view plain copy
  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.     public void run() {  
  21.         boolean launch = false;  
  22.         try {  
  23.               
  24.             if (!socket.processing.compareAndSet(falsetrue)) {  
  25.                 log.error("Unable to process socket. Invalid state.");  
  26.                 return;  
  27.             }  
  28.               
  29.             SocketState state = SocketState.OPEN;  
  30.             // Process the request from this socket  
  31.             if ( (!socket.isInitialized()) && (!setSocketOptions(socket.getSocket())) ) {   
  32.                 state = SocketState.CLOSED;  
  33.             }  
  34.             socket.setInitialized(true);  
  35.   
  36.             if ( (state != SocketState.CLOSED) ) {  
  37.                 state = (status==null)?handler.process(socket):handler.process(socket,status);  
  38.             }  
  39.             if (state == SocketState.CLOSED) {  
  40.                 // Close socket  
  41.                 if (log.isTraceEnabled()) {  
  42.                     log.trace("Closing socket:"+socket);  
  43.                 }  
  44.                 try {  
  45.                     socket.getSocket().close();  
  46.                 } catch (IOException e) {  
  47.                     // Ignore  
  48.                 }  
  49.             } else if (state == SocketState.OPEN){  
  50.                 socket.setKeptAlive(true);  
  51.                 socket.access();  
  52.                 //keepalive connection  
  53.                 //TODO - servlet3 check async status, we may just be in a hold pattern  
  54.                 launch = true;  
  55.             } else if (state == SocketState.LONG) {  
  56.                 socket.access();  
  57.                 waitingRequests.add(socket);  
  58.             }  
  59.         } finally {  
  60.             socket.processing.set(false);  
  61.             if (launch) getExecutor().execute(new SocketProcessor(socket));  
  62.             socket = null;  
  63.         }  
  64.         // Finish up this request  
  65.           
  66.     }  
  67.       
  68. }  

SocketProcessor线程专门用于处理Acceptor转交的Socket,其执行步骤如下:

  1. 调用setSocketOptions方法(见代码清单4)给Socket设置属性,从中可以看到设置属性用到了SocketProperties的setProperties方法(见代码清单5),状态更改为初始化完毕;
  2. 调用handler的process方法处理请求。在《Tomcat7.0源码分析——请求原理分析(上)》一文中我们讲过当处理Http11Protocol协议时,handler默认为Http11Protocol的内部类Http11ConnectionHandler;
  3. 请求处理完毕后,如果state等于SocketState.CLOSED,则关闭Socket;如果state等于SocketState.OPEN,则保持连接;如果state等于SocketState.LONG,则会作为长连接对待。
代码清单4

[java] view plain copy
  1. /** 
  2.  * Set the options for the current socket. 
  3.  */  
  4. protected boolean setSocketOptions(Socket socket) {  
  5.     // Process the connection  
  6.       
  7.     try {  
  8.         // 1: Set socket options: timeout, linger, etc  
  9.         socketProperties.setProperties(socket);  
  10.     } catch (SocketException s) {  
  11.         //error here is common if the client has reset the connection  
  12.         if (log.isDebugEnabled()) {  
  13.             log.debug(sm.getString("endpoint.err.unexpected"), s);  
  14.         }  
  15.         // Close the socket  
  16.         return false;  
  17.     } catch (Throwable t) {  
  18.         log.error(sm.getString("endpoint.err.unexpected"), t);  
  19.         // Close the socket  
  20.         return false;  
  21.     }  
  22.     try {  
  23.         // 2: SSL handshake  
  24.         serverSocketFactory.handshake(socket);  
  25.     } catch (Throwable t) {  
  26.         if (log.isDebugEnabled()) {  
  27.             log.debug(sm.getString("endpoint.err.handshake"), t);  
  28.         }  
  29.         // Tell to close the socket  
  30.         return false;  
  31.     }  
  32.     return true;  
  33. }  

代码清单5

[java] view plain copy
  1. public void setProperties(Socket socket) throws SocketException{  
  2.     if (rxBufSize != null)  
  3.         socket.setReceiveBufferSize(rxBufSize.intValue());  
  4.     if (txBufSize != null)  
  5.         socket.setSendBufferSize(txBufSize.intValue());  
  6.     if (ooBInline !=null)  
  7.         socket.setOOBInline(ooBInline.booleanValue());  
  8.     if (soKeepAlive != null)  
  9.         socket.setKeepAlive(soKeepAlive.booleanValue());  
  10.     if (performanceConnectionTime != null && performanceLatency != null &&  
  11.             performanceBandwidth != null)  
  12.         socket.setPerformancePreferences(  
  13.                 performanceConnectionTime.intValue(),  
  14.                 performanceLatency.intValue(),  
  15.                 performanceBandwidth.intValue());  
  16.     if (soReuseAddress != null)  
  17.         socket.setReuseAddress(soReuseAddress.booleanValue());  
  18.     if (soLingerOn != null && soLingerTime != null)  
  19.         socket.setSoLinger(soLingerOn.booleanValue(),  
  20.                 soLingerTime.intValue());  
  21.     if (soTimeout != null && soTimeout.intValue() >= 0)  
  22.         socket.setSoTimeout(soTimeout.intValue());  
  23.     if (tcpNoDelay != null)  
  24.         socket.setTcpNoDelay(tcpNoDelay.booleanValue());  
  25.     if (soTrafficClass != null)  
  26.         socket.setTrafficClass(soTrafficClass.intValue());  
  27. }  

以Http11ConnectionHandler为例,我们重点分析它是如何进一步处理Socket的。Http11ConnectionHandler的process方法,见代码清单6。

代码清单6

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

根据代码清单6,可见Http11ConnectionHandler的process方法的处理步骤如下:

  1. 从Socket的连接缓存connections(用于缓存长连接的Socket)中获取Socket对应的Http11Processor;如果连接缓存connections中不存在Socket对应的Http11Processor,则从可以循环使用的recycledProcessors(类型为ConcurrentLinkedQueue)中获取;如果recycledProcessors中也没有可以使用的Http11Processor,则调用createProcessor方法(见代码清单7)创建Http11Processor;
  2. 如果当前Connector配置了指定了SSLEnabled="true",那么还需要给Http11Processor设置SSL相关的属性;
  3. 如果Socket是异步的,则调用Http11Processor的asyncDispatch方法,否则调用Http11Processor的process方法;
  4. 请求处理完毕,如果Socket是长连接的,则将Socket和Http11Processor一起放入connections缓存,否则从connections缓存中移除Socket和Http11Processor。
代码清单7

[java] view plain copy
  1. protected Http11Processor createProcessor() {  
  2.     Http11Processor processor =  
  3.         new Http11Processor(proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint);  
  4.     processor.setAdapter(proto.adapter);  
  5.     processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());  
  6.     processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());  
  7.     processor.setTimeout(proto.getTimeout());  
  8.     processor.setDisableUploadTimeout(proto.getDisableUploadTimeout());  
  9.     processor.setCompressionMinSize(proto.getCompressionMinSize());  
  10.     processor.setCompression(proto.getCompression());  
  11.     processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents());  
  12.     processor.setCompressableMimeTypes(proto.getCompressableMimeTypes());  
  13.     processor.setRestrictedUserAgents(proto.getRestrictedUserAgents());  
  14.     processor.setSocketBuffer(proto.getSocketBuffer());  
  15.     processor.setMaxSavePostSize(proto.getMaxSavePostSize());  
  16.     processor.setServer(proto.getServer());  
  17.     register(processor);  
  18.     return processor;  
  19. }  

根据之前的分析,我们知道Socket的处理方式有异步和同步两种,分别调用Http11Processor的asyncDispatch方法或process方法,我们以同步处理为例,来看看接下来的处理逻辑。

同步处理

   Http11Processor的process方法(见代码清单8)用于同步处理,由于其代码很多,所以此处在代码后面追加一些注释,便于读者理解。这里面有一些关键方法重点拿出来解释下:

  1. InternalInputBuffer的parseRequestLine方法用于读取请求行;
  2. InternalInputBuffer的parseHeaders方法用于读取请求头;
  3. prepareRequest用于在正式处理请求之前,做一些准备工作,如根据请求头获取请求的版本号是HTTP/1.1还是HTTP/0.9、keepAlive是否为true等,还会设置一些输入过滤器用于标记请求、压缩等;
  4. 调用CoyoteAdapter的service方法处理请求。
代码清单8

[java] view plain copy
  1. RequestInfo rp = request.getRequestProcessor();  
  2. rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);  
  3. this.socket = socketWrapper;  
  4. inputBuffer.setInputStream(socket.getSocket().getInputStream());//设置输入流  
  5. outputBuffer.setOutputStream(socket.getSocket().getOutputStream());//设置输出流  
  6. int keepAliveLeft = maxKeepAliveRequests>0?socketWrapper.decrementKeepAlive():-1;//保持连接递减  
  7. int soTimeout = endpoint.getSoTimeout();//socket超时时间  
  8. socket.getSocket().setSoTimeout(soTimeout);//设置超时时间  
  9. boolean keptAlive = socketWrapper.isKeptAlive();//是否保持连接  
  10. while (started && !error && keepAlive) {  
  11.   
  12.      // Parsing the request header  
  13.      try {  
  14.          //TODO - calculate timeout based on length in queue (System.currentTimeMills() - wrapper.getLastAccess() is the time in queue)  
  15.          if (keptAlive) {//是否保持连接  
  16.              if (keepAliveTimeout > 0) {  
  17.                  socket.getSocket().setSoTimeout(keepAliveTimeout);  
  18.              }  
  19.              else if (soTimeout > 0) {  
  20.                  socket.getSocket().setSoTimeout(soTimeout);  
  21.              }  
  22.          }  
  23.    inputBuffer.parseRequestLine(false);//读取请求行  
  24.          request.setStartTime(System.currentTimeMillis());  
  25.          keptAlive = true;  
  26.          if (disableUploadTimeout) {  
  27.              socket.getSocket().setSoTimeout(soTimeout);  
  28.          } else {  
  29.              socket.getSocket().setSoTimeout(timeout);  
  30.          }  
  31.          inputBuffer.parseHeaders();//解析请求头  
  32.      } catch (IOException e) {  
  33.          error = true;  
  34.          break;  
  35.      } catch (Throwable t) {  
  36.          if (log.isDebugEnabled()) {  
  37.              log.debug(sm.getString("http11processor.header.parse"), t);  
  38.          }  
  39.          // 400 - Bad Request  
  40.          response.setStatus(400);  
  41.          adapter.log(request, response, 0);  
  42.          error = true;  
  43.      }  
  44.   
  45.      if (!error) {  
  46.          // Setting up filters, and parse some request headers  
  47.    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);  
  48.    try {  
  49.              prepareRequest();//对请求内容增加过滤器——协议、方法、请求头、host等  
  50.          } catch (Throwable t) {  
  51.              if (log.isDebugEnabled()) {  
  52.                  log.debug(sm.getString("http11processor.request.prepare"), t);  
  53.              }  
  54.              // 400 - Internal Server Error  
  55.              response.setStatus(400);  
  56.              adapter.log(request, response, 0);  
  57.              error = true;  
  58.          }  
  59.      }  
  60.   
  61.      if (maxKeepAliveRequests > 0 && keepAliveLeft == 0)  
  62.          keepAlive = false;  
  63.  // Process the request in the adapter  
  64.      if (!error) {  
  65.          try {  
  66.              rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);  
  67.              adapter.service(request, response); //将进一步处理交给CoyoteAdapter  
  68.              // Handle when the response was committed before a serious  
  69.              // error occurred.  Throwing a ServletException should both  
  70.              // set the status to 500 and set the errorException.  
  71.              // If we fail here, then the response is likely already  
  72.              // committed, so we can't try and set headers.  
  73.              if(keepAlive && !error) { // Avoid checking twice.  
  74.                  error = response.getErrorException() != null ||  
  75.                          statusDropsConnection(response.getStatus());  
  76.              }  
  77.   
  78.          } catch (InterruptedIOException e) {  
  79.              error = true;  
  80.          } catch (Throwable t) {  
  81.              log.error(sm.getString("http11processor.request.process"), t);  
  82.              // 500 - Internal Server Error  
  83.              response.setStatus(500);  
  84.              adapter.log(request, response, 0);  
  85.              error = true;  
  86.          }  
  87.      }  
  88.   
  89.      // Finish the handling of the request  
  90.      try {  
  91.          rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);  
  92.          // If we know we are closing the connection, don't drain input.  
  93.          // This way uploading a 100GB file doesn't tie up the thread   
  94.          // if the servlet has rejected it.  
  95.            
  96.          if(error && !async)  
  97.              inputBuffer.setSwallowInput(false);  
  98.          if (!async)  
  99.              endRequest();  
  100.      } catch (Throwable t) {  
  101.          log.error(sm.getString("http11processor.request.finish"), t);  
  102.          // 500 - Internal Server Error  
  103.          response.setStatus(500);  
  104.          adapter.log(request, response, 0);  
  105.          error = true;  
  106.      }  
  107.      try {  
  108.          rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);  
  109.      } catch (Throwable t) {  
  110.          log.error(sm.getString("http11processor.response.finish"), t);  
  111.          error = true;  
  112.      }  
  113.   
  114.      // If there was an error, make sure the request is counted as  
  115.      // and error, and update the statistics counter  
  116.      if (error) {  
  117.          response.setStatus(500);  
  118.      }  
  119.      request.updateCounters();  
  120.   
  121.      rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);  
  122.   
  123.      // Don't reset the param - we'll see it as ended. Next request  
  124.      // will reset it  
  125.      // thrA.setParam(null);  
  126.      // Next request  
  127.      if (!async || error) {  
  128.          inputBuffer.nextRequest();  
  129.          outputBuffer.nextRequest();  
  130.      }  
  131.        
  132.      //hack keep alive behavior  
  133.      break;  
  134.  }  
  135.   
  136.  rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);  
  137.  if (error) {  
  138.      recycle();  
  139.      return SocketState.CLOSED;  
  140.  } else if (async) {  
  141.      return SocketState.LONG;  
  142.  } else {  
  143.      if (!keepAlive) {  
  144.          recycle();  
  145.          return SocketState.CLOSED;  
  146.      } else {  
  147.          return SocketState.OPEN;  
  148.      }  
  149.  }   

从代码清单8可以看出,最后的请求处理交给了CoyoteAdapter,CoyoteAdapter的service方法(见代码清单9)用于真正处理请求。

代码清单9

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

从代码清单9可以看出,CoyoteAdapter的service方法的执行步骤如下:

  1. 创建Request与Response对象并且关联起来;
  2. 调用postParseRequest方法(见代码清单10)对请求进行解析;
  3. 将真正的请求处理交给Engine的Pipeline去处理,代码:connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
代码清单10

[java] view plain copy
  1.     /** 
  2.      * Parse additional request parameters. 
  3.      */  
  4.     protected boolean postParseRequest(org.apache.coyote.Request req,   
  5.                                        Request request,  
  6.                                    org.apache.coyote.Response res,   
  7.                                        Response response)  
  8.             throws Exception {  
  9. //   省略前边的次要代码  
  10.         parsePathParameters(req, request);  
  11.           
  12.         // URI decoding  
  13.         // %xx decoding of the URL  
  14.         try {  
  15.             req.getURLDecoder().convert(decodedURI, false);  
  16.         } catch (IOException ioe) {  
  17.             res.setStatus(400);  
  18.             res.setMessage("Invalid URI: " + ioe.getMessage());  
  19.             connector.getService().getContainer().logAccess(  
  20.                     request, response, 0true);  
  21.             return false;  
  22.         }  
  23.         // Normalization  
  24.         if (!normalize(req.decodedURI())) {  
  25.             res.setStatus(400);  
  26.             res.setMessage("Invalid URI");  
  27.             connector.getService().getContainer().logAccess(  
  28.                     request, response, 0true);  
  29.             return false;  
  30.         }  
  31.         // Character decoding  
  32.         convertURI(decodedURI, request);  
  33.         // Check that the URI is still normalized  
  34.         if (!checkNormalize(req.decodedURI())) {  
  35.             res.setStatus(400);  
  36.             res.setMessage("Invalid URI character encoding");  
  37.             connector.getService().getContainer().logAccess(  
  38.                     request, response, 0true);  
  39.             return false;  
  40.         }  
  41.   
  42.         // Set the remote principal  
  43.         String principal = req.getRemoteUser().toString();  
  44.         if (principal != null) {  
  45.             request.setUserPrincipal(new CoyotePrincipal(principal));  
  46.         }  
  47.   
  48.         // Set the authorization type  
  49.         String authtype = req.getAuthType().toString();  
  50.         if (authtype != null) {  
  51.             request.setAuthType(authtype);  
  52.         }  
  53.   
  54.         // Request mapping.  
  55.         MessageBytes serverName;  
  56.         if (connector.getUseIPVHosts()) {  
  57.             serverName = req.localName();  
  58.             if (serverName.isNull()) {  
  59.                 // well, they did ask for it  
  60.                 res.action(ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, null);  
  61.             }  
  62.         } else {  
  63.             serverName = req.serverName();  
  64.         }  
  65.         if (request.isAsyncStarted()) {  
  66.             //TODO SERVLET3 - async  
  67.             //reset mapping data, should prolly be done elsewhere  
  68.             request.getMappingData().recycle();  
  69.         }  
  70.         connector.getMapper().map(serverName, decodedURI,   
  71.                                   request.getMappingData());  
  72.         request.setContext((Context) request.getMappingData().context);  
  73.         request.setWrapper((Wrapper) request.getMappingData().wrapper);  
  74.   
  75.         // Filter trace method  
  76.         if (!connector.getAllowTrace()   
  77.                 && req.method().equalsIgnoreCase("TRACE")) {  
  78.             Wrapper wrapper = request.getWrapper();  
  79.             String header = null;  
  80.             if (wrapper != null) {  
  81.                 String[] methods = wrapper.getServletMethods();  
  82.                 if (methods != null) {  
  83.                     for (int i=0; i<methods.length; i++) {  
  84.                         if ("TRACE".equals(methods[i])) {  
  85.                             continue;  
  86.                         }  
  87.                         if (header == null) {  
  88.                             header = methods[i];  
  89.                         } else {  
  90.                             header += ", " + methods[i];  
  91.                         }  
  92.                     }  
  93.                 }  
  94.             }                                 
  95.             res.setStatus(405);  
  96.             res.addHeader("Allow", header);  
  97.             res.setMessage("TRACE method is not allowed");  
  98.             request.getContext().logAccess(request, response, 0true);  
  99.             return false;  
  100.         }  
  101.   
  102.         // Now we have the context, we can parse the session ID from the URL  
  103.         // (if any). Need to do this before we redirect in case we need to  
  104.         // include the session id in the redirect  
  105.         if (request.getServletContext().getEffectiveSessionTrackingModes()  
  106.                 .contains(SessionTrackingMode.URL)) {  
  107.               
  108.             // Get the session ID if there was one  
  109.             String sessionID = request.getPathParameter(  
  110.                     ApplicationSessionCookieConfig.getSessionUriParamName(  
  111.                             request.getContext()));  
  112.             if (sessionID != null) {  
  113.                 request.setRequestedSessionId(sessionID);  
  114.                 request.setRequestedSessionURL(true);  
  115.             }  
  116.         }  
  117.   
  118.         // Possible redirect  
  119.         MessageBytes redirectPathMB = request.getMappingData().redirectPath;  
  120.         if (!redirectPathMB.isNull()) {  
  121.             String redirectPath = urlEncoder.encode(redirectPathMB.toString());  
  122.             String query = request.getQueryString();  
  123.             if (request.isRequestedSessionIdFromURL()) {  
  124.                 // This is not optimal, but as this is not very common, it  
  125.                 // shouldn't matter  
  126.                 redirectPath = redirectPath + ";" +  
  127.                     ApplicationSessionCookieConfig.getSessionUriParamName(  
  128.                             request.getContext()) +  
  129.                     "=" + request.getRequestedSessionId();  
  130.             }  
  131.             if (query != null) {  
  132.                 // This is not optimal, but as this is not very common, it  
  133.                 // shouldn't matter  
  134.                 redirectPath = redirectPath + "?" + query;  
  135.             }  
  136.             response.sendRedirect(redirectPath);  
  137.             request.getContext().logAccess(request, response, 0true);  
  138.             return false;  
  139.         }  
  140.   
  141.         // Finally look for session ID in cookies and SSL session  
  142.         parseSessionCookiesId(req, request);  
  143.         parseSessionSslId(request);  
  144.         return true;  
  145.     }  

从代码清单10可以看出,postParseRequest方法的执行步骤如下:

  1. 解析请求url中的参数;
  2. URI decoding的转换(为了保证URL的可移植、完整性、可读性,通过ASCII字符集的有限子集对任意字符或数据进行编码、解码);
  3. 调用normalize方法判断请求路径中是否存在"\", "//", "/./"和"/../",如果存在则处理结束;
  4. 调用convertURI方法将字节转换为字符;
  5. 调用checkNormalize方法判断uri是否存在"\", "//", "/./"和"/../",如果存在则处理结束;
  6. 调用Connector的getMapper方法获取Mapper(已在《Tomcat7.0源码分析——请求原理分析(上)》一文中介绍),然后调用Mapper的map方法(见代码清单11)对host和context进行匹配(比如http://localhost:8080/manager/status会匹配host:localhost,context:/manager),其实质是调用internalMap方法;
  7. 使用ApplicationSessionCookieConfig.getSessionUriParamName获取sessionid的key,然后获取sessionid;
  8. 调用parseSessionCookiesId和parseSessionSslId方法查找cookie或者SSL中的sessionid。
代码清单11

[java] view plain copy
  1. public void map(MessageBytes host, MessageBytes uri,  
  2.                 MappingData mappingData)  
  3.     throws Exception {  
  4.   
  5.     if (host.isNull()) {  
  6.         host.getCharChunk().append(defaultHostName);  
  7.     }  
  8.     host.toChars();  
  9.     uri.toChars();  
  10.     internalMap(host.getCharChunk(), uri.getCharChunk(), mappingData);  
  11.   
  12. }  

CoyoteAdapter的service方法最后会将请求交给Engine的Pipeline去处理,我将在《Tomcat7.0源码分析——请求原理分析(下)》一文中具体讲解。