Tomcat架構詳解(二)

前幾天分析了一下Tomcat的架構和啓動過程,今天開始研究它的運轉機制。Tomcat最本質就是個能運行JSP/Servlet的Web服務器,因此最典型的應用就是用戶通過瀏覽器訪問服務器,Tomcat接收到請求後轉發給Servlet,由Servlet處理完後,把結果返回給客戶端。今天就專門解析一下這麼一個完整的請求的內部機理。

通過DEBUG,一路跟下來,發現Tomcat處理請求的核心過程是以下幾點:

  • 啓動的時候啓動預支持協議的Endpoint,Endpoint會起專門的線程監聽相應協議的請求,默認的情況下,會啓動JIoEndpoint,JIoEndpoint基於Java ServerSocket接收Http的請求
  • ServerSocket接收到客戶端請求的Socket後,一路包裝,並一路從Host一直傳遞到Wrapper,再請求到相應的Servlet

下面將重點解析以上兩個過程。

通過以前的分析(Tomcat源碼分析一)可知道當Tomcat啓動的時候會啓動Connector,此時Connector會通過ProtocolHandler把Endpoint啓動起來。默認情況下,Tomcat會啓動兩種Connector,分別是Http協議和AJP協議的,依次對應Http11Protocol和AjpProtocol,兩者都是啓動JIoEndpoint。下面看看JIoEndpoint的start方法:

  1. public void start() throws Exception {  
  2.     // Initialize socket if not done before  
  3.     if (!initialized) {  
  4.         init();  
  5.     }  
  6.     if (!running) {  
  7.         running = true;  
  8.         paused = false;  
  9.         // Create worker collection  
  10.         if (getExecutor() == null) {  
  11.             createExecutor();  
  12.         }  
  13.         // Start acceptor threads  
  14.         for (int i = 0; i < acceptorThreadCount; i++) {  
  15.             Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);  
  16.             acceptorThread.setPriority(threadPriority);  
  17.             acceptorThread.setDaemon(getDaemon());  
  18.             acceptorThread.start();  
  19.         }  
  20.     }  
  21. }  
 

以上代碼很清晰地表示啓動acceptorThreadCount個線程,每個線程由Acceptor代理,具體看看Acceptor的run方法:

  1. public void run() {  
  2.     // Loop until we receive a shutdown command  
  3.     while (running) {  
  4.         // Loop if endpoint is paused  
  5.         while (paused) {  
  6.             try {  
  7.                 Thread.sleep(1000);  
  8.             } catch (InterruptedException e) {  
  9.                 // Ignore  
  10.             }  
  11.         }  
  12.         // Accept the next incoming connection from the server socket  
  13.         try {  
  14.             Socket socket = serverSocketFactory.acceptSocket(serverSocket);  
  15.             serverSocketFactory.initSocket(socket);  
  16.             // Hand this socket off to an appropriate processor  
  17.             if (!processSocket(socket)) {  
  18.                 // Close socket right away  
  19.                 try {  
  20.                     socket.close();  
  21.                 } catch (IOException e) {  
  22.                     // Ignore  
  23.                 }  
  24.             }  
  25.         }catch ( IOException x ) {  
  26.             if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);  
  27.         } catch (Throwable t) {  
  28.             log.error(sm.getString("endpoint.accept.fail"), t);  
  29.         }  
  30.         // The processor will recycle itself when it finishes  
  31.     }  
  32. }  
 

由此可得到這麼一個結論:Tomcat就是通過ServerSocket監聽Socket的方式來接收客戶端請求的。具體代碼就無需我解析了,稍微瞭解Java net的人都能看懂以上代碼,Tomcat就是用最標準和最基礎的Socket調用方法來處理網絡請求的。找到處理請求的源頭後下面要做的是事情就簡單了,打好斷點,在瀏覽器裏請求一個最簡單的Hello world,一路debug下去。一路跟下來,主流程的時序圖如下所示:

從上圖可知,以上過程可分解成以下三個最主要的核心點:

  • 基於Http1.1協議對Socket的解析和包裝
  • StandardEngineValve、StandardHostValve、StandardContextValve和StandardWrapperValve四種Valve的一路inoke。四種不同層次的Valve做了不同層次的處理和封裝
  • 基於責任鏈模式ApplicationFilterChain實現Filter攔截和實際Servlet的請求

以上三個核心點都是內容非常豐富的可研究點,會在以後幾天逐一進行剖析。

發佈了90 篇原創文章 · 獲贊 19 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章