Tomcat源码分析(二)--连接处理

系统 1774 0
本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996 

目标:在这篇文章希望搞明白http请求到tomcat后是怎么由连接器转交到容器的?

      在上一节里已经启动了一个HttpConnector线程,并且也启动了固定数量的HttpProcessor线程。HttpConnector用来等待http连接,得到http连接后交给其中的一个HttpProcessor线程来处理。接下里具体看一下HttpConnector是怎么得到连接得,以及HttpProcessor是怎么处理的。当启动了HttpConnector线程后(在上一节已经知道怎么启动了),便在它的run方法里面循环等待:

  1. public   void  run() {  
  2.      // Loop until we receive a shutdown command   
  3.      while  (!stopped) {  
  4.          // Accept the next incoming connection from the server socket   
  5.         Socket socket =  null ;  
  6.          try  {  
  7.             socket = serverSocket.accept();  
  8.              if  (connectionTimeout >  0 )  
  9.                 socket.setSoTimeout(connectionTimeout);  
  10.             socket.setTcpNoDelay(tcpNoDelay);  
  11.         }  catch  (AccessControlException ace) {  
  12.             log( "socket accept security exception" , ace);  
  13.              continue ;  
  14.         }  catch  (IOException e) {  
  15.              try  {  
  16.                  // If reopening fails, exit   
  17.                  synchronized  (threadSync) {  
  18.                      if  (started && !stopped)  
  19.                         log( "accept error: " , e);  
  20.                      if  (!stopped) {  
  21.                         serverSocket.close();  
  22.                         serverSocket = open();  
  23.                     }  
  24.                 }  
  25.                   
  26.             }  catch  (IOException ioe) {  
  27.                 log( "socket reopen, io problem: " , ioe);  
  28.                  break ;  
  29.             }  catch  (KeyStoreException kse) {  
  30.                 log( "socket reopen, keystore problem: " , kse);  
  31.                  break ;  
  32.             }  catch  (NoSuchAlgorithmException nsae) {  
  33.                 log( "socket reopen, keystore algorithm problem: " , nsae);  
  34.                  break ;  
  35.             }  catch  (CertificateException ce) {  
  36.                 log( "socket reopen, certificate problem: " , ce);  
  37.                  break ;  
  38.             }  catch  (UnrecoverableKeyException uke) {  
  39.                 log( "socket reopen, unrecoverable key: " , uke);  
  40.                  break ;  
  41.             }  catch  (KeyManagementException kme) {  
  42.                 log( "socket reopen, key management problem: " , kme);  
  43.                  break ;  
  44.             }  
  45.   
  46.              continue ;  
  47.         }  
  48.   
  49.          // Hand this socket off to an appropriate processor   
  50.         HttpProcessor processor = createProcessor();  
  51.          if  (processor ==  null ) {  
  52.              try  {  
  53.                 log(sm.getString( "httpConnector.noProcessor" ));  
  54.                 socket.close();  
  55.             }  catch  (IOException e) {  
  56.                 ;  
  57.             }  
  58.              continue ;  
  59.         }  
  60.        
  61.         processor.assign(socket);  
  62.   
  63.     }  
  64.   
  65.      // Notify the threadStop() method that we have shut ourselves down   
  66.    
  67.      synchronized  (threadSync) {  
  68.         threadSync.notifyAll();  
  69.     }  
  70.   
  71. }  
这里很关键的就是socket = serverSocket.accept();和processor.assign(socket); 在循环里面内,serverSocket.accept();负责接收http请求然后赋值给socket,最后交给其中一个processor处理。这里processor并不是等到需要的时候再实例化,而是在HttpConnector初始化的时候已经有了若干个processor,在httpConnector里有这样一个声明:

  1. private  Stack processors =  new  Stack();  
表明httpConnector里面持有一个包含HttpProcessor对象的栈,需要的时候拿出来就是了。看一下createProcessor函数就能比较明白了:

  1. private  HttpProcessor createProcessor() {  
  2.       synchronized  (processors) {  
  3.           if  (processors.size() >  0 ) {  
  4.               return  ((HttpProcessor) processors.pop());  //从processors栈中弹出一个processor   
  5.          }  
  6.           if  ((maxProcessors >  0 ) && (curProcessors < maxProcessors)) {  
  7.               return  (newProcessor());  
  8.          }  else  {  
  9.               if  (maxProcessors <  0 ) {  
  10.                   return  (newProcessor());  
  11.              }  else  {  
  12.                   return  ( null );  
  13.              }  
  14.          }  
  15.      }  
  16.   
  17.  }  

接下来由processor.assign(socket); 记住这个方法是异步的,不需要等待HttpProcessor来处理完成,所以HttpConnector才能不间断的传入Http请求,在HttpProcessor里有两个方法比较重要,这两个方法协调处理了由HttpConnector传来的socket:

  1.   synchronized   void  assign(Socket socket) {  
  2.   
  3.      // Wait for the Processor to get the previous Socket   
  4.      while  (available) {  
  5.          try  {  
  6.             wait();  
  7.         }  catch  (InterruptedException e) {  
  8.         }  
  9.     }  
  10.   
  11.      // Store the newly available Socket and notify our thread   
  12.      this .socket = socket;  
  13.     available =  true ;  
  14.     notifyAll();  
  15.   
  16.      if  ((debug >=  1 ) && (socket !=  null ))  
  17.         log( " An incoming request is being assigned" );  
  18.   
  19. }  
  20.   
  21.   
  22. private   synchronized  Socket await() {  
  23.   
  24.      // Wait for the Connector to provide a new Socket   
  25.      while  (!available) {  
  26.          try  {  
  27.             wait();  
  28.         }  catch  (InterruptedException e) {  
  29.         }  
  30.     }  
  31.   
  32.      // Notify the Connector that we have received this Socket   
  33.     Socket socket =  this .socket;  
  34.     available =  false ;  
  35.     notifyAll();  
  36.   
  37.      if  ((debug >=  1 ) && (socket !=  null ))  
  38.         log( "  The incoming request has been awaited" );  
  39.   
  40.      return  (socket);  
  41.   
  42. }  

看一下HttpProcessor的run方法:

  1. public   void  run() {  
  2.   
  3.         // Process requests until we receive a shutdown signal   
  4.         while  (!stopped) {  
  5.   
  6.             // Wait for the next socket to be assigned   
  7.            Socket socket = await();  
  8.             if  (socket ==  null )  
  9.                 continue ;  
  10.   
  11.             // Process the request from this socket   
  12.             try  {  
  13.                process(socket);  
  14.            }  catch  (Throwable t) {  
  15.                log( "process.invoke" , t);  
  16.            }  
  17.   
  18.             // Finish up this request   
  19.            connector.recycle( this );  
  20.   
  21.        }  
  22.   
  23.         // Tell threadStop() we have shut ourselves down successfully   
  24.         synchronized  (threadSync) {  
  25.            threadSync.notifyAll();  
  26.        }  
  27.   
  28.    }  

很明显,在它的run方法一开始便是调用上面的await方法来等待(因为一开始available变量为false),所以HttpProcessor会一直阻塞,直到有线程来唤醒它。当从HttpConnector中调用processor.assign(socket),会把socket传给此HttpProcessor对象,并设置available为true,调用notifyAll()唤醒该processor线程以处理socket。同时,在await方法中又把available设置成false,因此又回到初始状态,即可以重新接受socket。

这里处理socket的方法是process(socket),主要作用有两点,1:解析这个socket,即解析http请求,包括请求方法,请求协议等,以填充request,response对象(是不是很熟悉,在servlet和jsp开发经常用到的request,response对象就是从这里来的)。2:传入request,response对象给和HttpConnector绑定的容器,让容器来调用invoke方法进行处理。process方法主要的代码如下:

  1. private   void  process(Socket socket) {  
  2.                    input =  new  SocketInputStream(socket.getInputStream(),  
  3.                                           connector.getBufferSize());  
  4.                     //解析一下连接的地址,端口什么的   
  5.                     parseConnection(socket);  
  6.                      //解析请求头的第一行,即:方法,协议,uri   
  7.                     parseRequest(input, output);  
  8.                      if  (!request.getRequest().getProtocol()  
  9.                         .startsWith( "HTTP/0" ))  
  10.                     parseHeaders(input); //解析http协议的头部   
  11.                     ..............................................  
  12.                     connector.getContainer().invoke(request, response);  
  13.                     .............................................  
  14. }  
在那些parse××方法里面会对request,response对象进行初始化,然后调用容器的invoke方法进行处理,至此,http请求过来的连接已经完美的转交给容器处理,容器剩下的问题就是要最终转交给哪个servlet或者jsp的问题。前面我们知道,一个连接会跟一个容器相连,一个级别大的容器会有一个或者多个子容器,最小的容器是Wrapper,对应一个servlet,在这里我们只要知道请求的路径决定了最终会选择哪个wrapper,wrapper最终会调用servlet的。至少一开始提出来的问题已经明白了。这里又有一个问题,在调用invoke方法是有这样的connector.getContainer的代码,即通过连接器得到跟它关联的容器,这个连接器是什么时候跟容器关联上的? 详见下篇: Tomcat源码分析(三)--连接器是如何与容器关联的?

Tomcat源码分析(二)--连接处理


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论