Tomcat源碼分析(二)--鏈接處理

目標:在這篇文章但願搞明白http請求到tomcat後是怎麼由鏈接器轉交到容器的?java

      在上一節裏已經啓動了一個HttpConnector線程,而且也啓動了固定數量的HttpProcessor線程。HttpConnector用來等待http鏈接,獲得http鏈接後交給其中的一個HttpProcessor線程來處理。接下里具體看一下HttpConnector是怎麼獲得鏈接得,以及HttpProcessor是怎麼處理的。當啓動了HttpConnector線程後(在上一節已經知道怎麼啓動了),便在它的run方法裏面循環等待:tomcat

[java] view plaincopyprint?app

  1. public void run() {  異步

  2.     // Loop until we receive a shutdown command  socket

  3.     while (!stopped) {  jsp

  4.         // Accept the next incoming connection from the server socket  tcp

  5.         Socket socket = null;  ide

  6.         try {  函數

  7.             socket = serverSocket.accept();  oop

  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裏有這樣一個聲明:

[java] view plaincopyprint?

  1. private Stack processors = new Stack();  

代表httpConnector裏面持有一個包含HttpProcessor對象的棧,須要的時候拿出來就是了。看一下createProcessor函數就能比較明白了:

[java] view plaincopyprint?

  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:

[java] view plaincopyprint?

  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方法:

[java] view plaincopyprint?

  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方法主要的代碼以下:

[java] view plaincopyprint?

  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源碼分析(三)--鏈接器是如何與容器關聯的?

相關文章
相關標籤/搜索