前幾天分析了一下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方法:
- public void start() throws Exception {
- // Initialize socket if not done before
- if (!initialized) {
- init();
- }
- if (!running) {
- running = true;
- paused = false;
- // Create worker collection
- if (getExecutor() == null) {
- createExecutor();
- }
- // Start acceptor threads
- for (int i = 0; i < acceptorThreadCount; i++) {
- Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
- acceptorThread.setPriority(threadPriority);
- acceptorThread.setDaemon(getDaemon());
- acceptorThread.start();
- }
- }
- }
以上代碼很清晰地表示啓動acceptorThreadCount個線程,每個線程由Acceptor代理,具體看看Acceptor的run方法:
- public void run() {
- // Loop until we receive a shutdown command
- while (running) {
- // Loop if endpoint is paused
- while (paused) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- // Accept the next incoming connection from the server socket
- try {
- Socket socket = serverSocketFactory.acceptSocket(serverSocket);
- serverSocketFactory.initSocket(socket);
- // Hand this socket off to an appropriate processor
- if (!processSocket(socket)) {
- // Close socket right away
- try {
- socket.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }catch ( IOException x ) {
- if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
- } catch (Throwable t) {
- log.error(sm.getString("endpoint.accept.fail"), t);
- }
- // The processor will recycle itself when it finishes
- }
- }
由此可得到這麼一個結論:Tomcat就是通過ServerSocket監聽Socket的方式來接收客戶端請求的。具體代碼就無需我解析了,稍微瞭解Java net的人都能看懂以上代碼,Tomcat就是用最標準和最基礎的Socket調用方法來處理網絡請求的。找到處理請求的源頭後下面要做的是事情就簡單了,打好斷點,在瀏覽器裏請求一個最簡單的Hello world,一路debug下去。一路跟下來,主流程的時序圖如下所示:
從上圖可知,以上過程可分解成以下三個最主要的核心點:
- 基於Http1.1協議對Socket的解析和包裝
- StandardEngineValve、StandardHostValve、StandardContextValve和StandardWrapperValve四種Valve的一路inoke。四種不同層次的Valve做了不同層次的處理和封裝
- 基於責任鏈模式ApplicationFilterChain實現Filter攔截和實際Servlet的請求
以上三個核心點都是內容非常豐富的可研究點,會在以後幾天逐一進行剖析。