Tomcat(三):tomcat處理連接的詳細過程

来源:https://www.cnblogs.com/f-ck-need-u/archive/2018/02/03/8408670.html
-Advertisement-
Play Games

本文目錄:1. Tomcat組件體繫結構2. Tomcat和httpd/nginx在監聽和處理請求上的區別 2.1 Tomcat如何處理併發請求3. Tomcat處理jsp動態資源的過程4. Tomcat處理靜態資源的過程 tomcat可以處理靜態資源的請求,也可以通過servlet處理動態資源的請 ...


本文目錄:
1. Tomcat組件體繫結構
2. Tomcat和httpd/nginx在監聽和處理請求上的區別
  2.1 Tomcat如何處理併發請求
3. Tomcat處理jsp動態資源的過程
4. Tomcat處理靜態資源的過程

tomcat可以處理靜態資源的請求,也可以通過servlet處理動態資源的請求。處理jsp動態資源時,先通過jasper組件(具體的是JspServlet)將jsp翻譯成java源代碼並編譯成class後運行。需要知道的是,靜態資源也一樣是通過servlet處理的,只不過它使用的servlet是定義在$catalina_home/conf/web.xml中預設的servlet。本文將詳細分析tomcat如何處理客戶端請求(併發)以及如何處理動、靜態資源。

1.Tomcat組件體繫結構

如下兩圖:上面的圖是tomcat組件體系的簡圖,下麵的圖是Service組件細化後的圖。

其中:

  1. server組件是管理tomcat實例的組件,可以監聽一個埠,從此埠上可以遠程向該實例發送shutdown關閉命令。
  2. service組件是一個邏輯組件,用於綁定connector和container,有了service表示可以向外提供服務,就像是一般的daemon類服務的service。可以認為一個service就啟動一個JVM,更嚴格地說,一個engine組件才對應一個JVM(定義負載均衡時,jvmRoute就定義在Engine組件上用來標識這個JVM),只不過connector也工作在JVM中。
  3. connector組件是監聽組件,它有四個作用:
    • (1).開啟監聽套接字,監聽外界請求,並建立TCP連接;
    • (2).使用protocolHandler解析請求中的協議和埠等信息,如http協議、AJP協議;
    • (3).根據解析到的信息,使用processer將請求數據轉發給綁定的Engine;
    • (4).接收響應數據並返回給客戶端。
  4. container是容器,它是一類組件,在配置文件(如server.xml)中沒有體現出來。它包含4個容器類組件:engine容器、host容器、context容器和wrapper容器。
  5. engine容器用於從connector組件處接收轉發過來的請求,然後按照分析的結果將相關參數傳遞給匹配出的虛擬主機。engine還用於指定預設的虛擬主機。
  6. host容器定義虛擬主機,由於tomcat主要是作為servlet容器的,所以為每個webapp指定了它們的根目錄appBase。
  7. context容器主要是根據path和docBase獲取一些信息,將結果交給其內的wrapper組件進行處理(它提供wrapper運行的環境,所以它叫上下文context)。一般來說,都採用預設的標準wrapper類,因此在context容器中幾乎不會出現wrapper組件。
  8. wrapper容器對應servlet的處理過程。它開啟servlet的生命周期,根據context給出的信息以及解析web.xml中的映射關係,負責裝載相關的類,初始化servlet對象init()、執行servlet代碼service()以及服務結束時servlet對象的銷毀destory()。
  9. executor組件為每個Service組件提供線程池,使得Engine可以從線程池中獲取線程處理請求,從而實現tomcat的併發處理能力。一定要註意,Executor的線程池大小是為Engine組件設置,而不是為Connector設置的,Connector的線程數量由Connector組件的acceptorThreadCount屬性來設置。如果要在配置文件中設置該組件,則必須設置在Connector組件的前面,以便在Connector組件中使用executor屬性來引用配置好的Executor組件。如果不顯式設置,則採用Connector組件上的預設配置,預設配置如下:
    • (1).maxThreads:最大線程數,預設值200。
    • (2).minSpareThreads:最小空閑線程數,預設值25。
    • (3).maxIdleTime:空閑線程的線程空閑多長時間才會銷毀,預設值60000即1分鐘。
    • (4).prestartminSpareThreads:是否啟動executor時就直接創建等於最小空閑線程數的線程,預設值為false,即只在有連接請求進入時才會創建。

根據上面描述的tomcat組件體繫結構,處理請求的大致過程其實很容易推導出來:

Client(request)-->Connector-->Engine-->Host-->Context-->Wrapper(response data)-->Connector(response header)-->Client

2.Tomcat和httpd/nginx在監聽和處理請求上的區別

在監聽和處理請求上,tomcat和httpd/nginx等服務程式不一樣,而且是巨大的區別。因此,在理解處理請求時,萬萬不可將httpd/nginx的處理模式套在tomcat上。

關於httpd/nginx等服務程式處理連接時的過程,此處僅簡單說明以體現它們和tomcat的不同之處,詳細內容可參見我另一篇文章:不可不知的socket和TCP連接過程

(1).httpd/nginx等都是監聽進程/線程負責監聽,當監聽到連接請求時,將生成一個新的已連接套接字放進一個稱為已連接隊列中,然後監聽進程/線程繼續回去監聽。而負責處理請求的共作進程/線程則從該隊列中獲取已連接套接字並與客戶端建立TCP連接,然後與客戶端進行通信,包括接收客戶端的資源請求數據、構建和響應數據給客戶端。

(2).tomcat雖然也將監聽和處理請求的工作分別使用不同的組件進行處理,但connector線程監聽到請求就直接建立TCP連接,並一直與客戶端保持該連接。connector線程會分析請求並將結果轉發給與之綁定的Engine組件,Engine線程負責處理請求以及構建響應數據,但Engine組件不會和客戶端建立任何連接。Engine的一切數據來源都是Connector,客戶端任何一次資源請求都會發送到connector上,並從connector轉發給Engine。Engine構建響應後,再次將響應數據轉發給Connector,並由Connector做一些處理(如加上首部欄位)回覆給客戶端。

只要明確一點即可推導出tomcat的連接和請求處理機制:任何一次從外界流入的請求都必將經過connector,任何一次從本地流出的響應數據也都必將經過connector。這正是連接器的意義所在──連接客戶端和服務端servlet。

2.1 tomcat如何處理併發請求

connector組件支持4種IO協議類型:同步阻塞BIO、同步非阻塞NIO、非同步非阻塞NIO2、apache基金會提供的IO模型APR(IO模型只是APR類庫的其中一種功能模塊)。它們的區別如下圖所示,除了BIO,其他IO模型在接受新請求上都是非阻塞的,因此這裡不考慮BIO,而且現在也不會有人將connector設置成BIO模式。

該表中,最需要關註的是"Wait for next Request"行,NIO/NIO2/APR都是Non Blocking,這表示正在處理某個請求時不會被阻塞,可以接收額外的請求,這是tomcat實現併發處理請求的關鍵。

再來看connector組件和併發數量有關的設置選項:

acceptorThreadCount:用於接收連接請求的線程數。預設值為1。多核CPU系統應該增大該值,另外由於長連接的存在,也應該考慮增大該值。
maxThreads:線程池中最多允許存在多少線程用於處理請求。預設值為200。它是最大併發處理的數量,但不影響接收線程接收更多的連接。
maxConnections:服務端允許接收和處理的最大連接數。當達到該值後,操作系統還能繼續接收額外acceptCount個的連接請求,但這些連接暫時不會被處理。當Connector類型為BIO模型時的預設值等於maxThread的值,當為NIO/NIO2模型時的預設值為10000,當APR時預設長度為8192。
acceptCount:當所有請求處理線程都處於忙碌狀態時,連接請求將進入等待隊列,該值設置等待隊列的長度。當達到隊列最大值後,如果還有新連接請求進入,則會被拒絕。預設隊列長度為100。

從上面幾個屬性的意義來分析併發機制:

  • (1).connector中最多有acceptorThreadCount個專門負責監聽、接收連接請求並建立TCP連接的線程,這些線程是非阻塞的(不考慮BIO)。當和某客戶端建立TCP連接後,可以繼續去監聽或者將Engine返回的數據發送給客戶端或者處理其它事情。
  • (2).線程池中的最大線程數maxThreads決定了某一刻允許處理的最大併發請求數,這是專門負責處理connector轉發過來的請求的線程,可以認為這些線程專門是為Engine組件服務的(因此我將其稱之為Engine線程)。註意,maxThreads決定的是某一刻的最大併發處理能力,但不意味著maxThreads數量的線程只能處理maxThreads數量的請求,因為這些Engine線程也是非阻塞的,當處理某個請求時出現IO等待時,它不會阻塞,而是繼續處理其它請求。也就是說,每個請求都占用一個Engine線程直到該客戶端的所有請求處理完畢,但每個Engine線程可以處理多個請求。同時還能推測出,每個connector線程可以和多個Engine線程綁定(connector線程的數量遠少於Engine線程的數量)。
  • (3).當併發請求數量逐漸增多,tomcat處理能力的極限由maxConnector決定,這個值是由maxThreads和acceptorThreadCount以及非阻塞特性同時決定的。由於非阻塞特性,無論是connector線程還是Engine線程,都能不斷接收、處理新請求。它的預設值看上去很大(10000或8192),但分配到每個線程上的數量並不大。假設不考慮監聽線程對數量的影響,僅從處理線程上來看,10000個連接分配給200個處理線程,每個處理線程可以輪詢處理50個請求。和nginx預設的一個worker線程允許1024個連接相比,已經很少了,當然,因為架構模型不一樣,它們沒有可比性。
  • (4).當併發請求數量繼續增大,tomcat還能繼續接收acceptCount個請求,但不會去建立連接,所以也不會去處理。實際上,這些請求不是tomcat接收的,而是操作系統接收的,接收後放入到由Connector創建的隊列中,當tomcat有線程可以處理新的請求了再去隊列中取出並處理。

再來細分一下tomcat和httpd/nginx的不同點:

  • (1).httpd/nginx的監聽者只負責監聽和產生已連接套接字,不會和客戶端直接建立TCP連接。而tomcat的監聽者connector線程不僅會監聽,還會直接建立TCP連接,且一直處於ESTABLISHED狀態直到close
  • (2).httpd/nginx的工作進程/線程首先從已連接套接字隊列中獲取已連接套接字,並與客戶端建立TCP連接,然後和客戶端通信兵處理請求、響應數據。而tomcat的工作線程(Engine線程)只接受來自connector轉發過來的請求,處理完畢後還會將響應數據轉發回connector線程,由connector將響應數據傳輸給客戶端(和客戶端的所有通信數據都必須經過連接器connector來傳輸)
  • (3).不難推斷出,一個Connector線程可以和多個客戶端建立TCP連接,也可以和多個Engine線程建立綁定關係,而一個Engine線程可以處理多個請求。如果不理解併發處理機制,這一點很容易被"Connector組件和Engine組件綁定在一起組成Service組件"這句話誤導。這句話的意思並不是要求它們1:1對應,就像httpd/nginx也一樣,一個監聽者可能對應多個工作者。

因此,tomcat處理連接的過程如下圖所示,其中我把Engine線程處理請求的過程用"Engine+N"來表示,例如Engine線程1下的Engine1表示該Engine線程處理的某個請求,Engine2表示該線程處理的另一個請求。

3.Tomcat處理jsp動態資源的過程

假設tomcat的配置如下,其中項目名稱為"xiaofang"。

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

<Engine name="Catalina" defaultHost="localhost">
    <Host name="www.xiaofang.com"  appBase="webapps/xiaofang"
          unpackWARs="true" autoDeploy="true">
        <Context path="" docBase="" reloadable="true" />
        <Context path="/xuexi" docBase="xuexi" reloadable="true" />
    </Host>

    <Host name="localhost"  appBase="webapps"
          unpackWARs="true" autoDeploy="true">
    </Host>
</Engine>

當客戶端訪問http://www.xiaofang.com:8080/xuexi/abc.jsp時,其請求的是$CATALINA_HOME/webapps/xiaofang/xuexi/abc.jsp文件。

(1).Connector組件扮演的角色。

Connector組件首先監聽到該請求,於是建立TCP連接,並分析該請求。Connector分析請求的內容包括請求的協議、埠、參數等。因為這裡沒考慮集群問題,因此只可能是http協議而不可能是ajp協議的請求。分析後,將請求和相關參數轉發給關聯的Engine組件進行處理。

(2).Engine組件扮演的角色。

Engine組件主要用於將請求分配到匹配成功的虛擬主機上,如果沒有能匹配成功的,則分配到預設虛擬主機上。對於上面的請求,很顯然將分配到虛擬主機www.xiaofang.com上。

(3).Host組件扮演的角色。

Host組件收到Engine傳遞過來的請求參數後,將對請求中的uri與Context中的path進行匹配,如果和某個Context匹配成功,則將請求交給該Context處理。如果匹配失敗,則交給path=""對應的Context來處理。所以,根據匹配結果,上面的請求將交給<Context path="/xuexi" docBase="xuexi" />進行處理。

註意,這次的uri匹配是根據path進行的匹配,它是目錄匹配,不是文件匹配也就是說,只匹配到uri中的xuexi就結束匹配。之所以要明確說明這一點,是因為後面還有一次文件匹配,用於決定交給哪個Servlet來處理。

(4).Context和Wrapper組件扮演的角色。

到了這裡,就算真正到了Servlet程式運行的地方了,相比於前面幾個組件,這裡的過程也更複雜一些。

請求http://www.xiaofang.com:8080/xuexi/abc.jsp經過Host的uri匹配後,分配給<Context path="/xuexi" docBase="xuexi" />進行處理,此時已經匹配了url中的目錄,剩下的是abc.jsp。abc.jsp也需要匹配,但這個匹配是根據web.xml中的配置進行匹配的。

首先,從項目名為xiaofang的私有web.xml中進行查找,即webapps/xiaofang/WEB-INF/web.xml。由於此處僅為簡單測試,因此並沒有該文件。

於是從全局web.xml即$CATALINA_HOME/conf/web.xml中匹配abc.jsp。以下是web.xml中能匹配到該文件名的配置部分。

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>

首先根據<servlet-mapping>中的url-pattern進行文件匹配,發現該url匹配的是servlet-name為"jsp"的servlet,然後再找到與該名稱對應的<servlet>標簽段,發現處理該動態資源的類為org.apache.jasper.servlet.JspServlet,於是找到該類對應的class文件,該class文件歸檔在$catalina_home/lib/jasper.jar中。

JspServlet程式的作用是將jsp文件翻譯成java源代碼文件,並放在$catalina_home/work目錄下。然後將該java源文件進行編譯,編譯後的class文件也放在work目錄下。這個class文件就是abc.jsp最終要執行的servlet小程式。

[root@xuexi ~]# ls /usr/local/tomcat/work/Catalina/www.xiaofang.com/xuexi/org/apache/jsp/
index_jsp.class  index_jsp.java  new_

在翻譯後的servlet小程式中,不僅會輸出業務邏輯所需的數據,還會輸出html/css代碼,這樣一來,客戶端接收到的數據都將是排版好的。

4.Tomcat處理靜態資源的過程

對於tomcat來說,無論是動態還是靜態資源,都是經過servlet處理的。只不過處理靜態資源的servlet是預設的servlet而已。

在$catalina_home/conf/web.xml中關於靜態資源處理的配置如下。

<!-- The mapping for the default servlet -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
</servlet>

需要記住的是,web.xml中的url-pattern是文件匹配,而server.xml中的<Context path="URL-PATTERN" />是目錄匹配。

上面web.xml中的<url-pattern>/</url-pattern>表示的是預設servlet。這意味著,當web.xml中沒有servlet-mapping能匹配請求url中的路徑時,將匹配servlet-name,即名為default的servlet。然後找到處理default的類為org.apache.catalina.servlets.DefaultServlet,該類的class文件歸檔在$catalina_home/lib/catalina.jar中。該servlet不像JspServlet會翻譯jsp文件,它只有最基本的作用:原樣輸出請求文件中的內容給客戶端。

例如,根據前面的配置,下麵幾個請求都將採用預設servlet進行處理,即當作靜態資源處理。

http://www.xiaofang.com:8080/xuexi/index.html
http://www.xiaofang.com:8080/xuexi/abc.js
http://www.xiaofang.com:8080/xuexi/index
http://www.xiaofang.com:8080/xuexi/index.txt

http://www.xiaofang.com:8080/xuexi則不一定,因為tomcat中預設的index文件包含index.jsp和index.html,而index.jsp排在index.html的前面,只有不存在index.jsp時才請求index.html。

 

回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html
轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/8408670.html

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在linux系統下我們經常使用ll、la命令。但在mac系統時缺沒有。 提示:-bash: ll: command not found。 這是因為ll、la不是真的命令,而是一些常用命令和參數搭配的別名。所以我們在Mac OS下配置下就好了。 1、跳到個人目錄下麵 lunadeMacBook-Air ...
  • 孤兒進程: 因父親進程先退出而導致一個子進程被 init 進程收養的進程為孤兒進程,即孤兒進程的父親更改為 init 進程,該進程在孤兒進程退出後回收它的內核空間資源。 僵死進程: 進程已經退出,但它的父親進程還沒有回收內核資源的進程為僵死進程,即該進程在內核空間的 PCB(進程式控制制塊) 沒有釋放。 ...
  • 第一種方法 一、下載解壓 二、安裝開發包組和相關依賴包 三、編譯apr和apr-util httpd是依賴於apr的,所以先編譯apr和apr-util 1、編譯apr 2、編譯apr-util 四、編譯httpd 五、環境變數 六、創建用戶和組 七、修改配置文件 八、啟動腳本 第二種方法 一、下載 ...
  • tomcat和大多數服務程式的管理不一樣,tomcat更適合使用圖形管理界面進行管理,例如在不停止tomcat的情況下動態部署新的webapp或重新載入webapp。如果不使用圖形管理工具,tomcat雖然也可以配置自動部署(autoDeploy="true"),但我們卻無法掌握它重新部署的時刻。 ...
  • 部署apche服務的步驟: 準備環境: 關閉防火牆 :service iptables stop 設置開機關閉防火牆:chkconfig iptables off 關閉selinux :setenforce 0 設置開機關閉selinux :修改配置文件 vim /etc/sysconfig/sel ...
  • ARM公司提供了Cortex-M系列處理器的官方性能對比數據,包括Dhrystone和CoreMark標準 ...
  • ssh服務為了管理異地linux主機服務而生,使用ssh服務管理遠程主機 查看linux主機ssh軟體包命令: rpm -qa | grep openssh ssh服務分為伺服器端和客戶端 伺服器端: openshh-server 客戶端: openshh-clients xshell secure ...
  • ocx 控制項在安裝包運行時會自動註冊,如果安裝包沒有註冊成功,需要進行手動註冊 PPAXSignToolSDK.ocx 這個OCX控制項是瀏覽器下使用手寫板時調用的,使用前必須先註冊,,不然瀏覽器下版本無法正常工作。 正常的方式在32位系統下可行, 但是在64位系統下是不可行的. 在64位系統中正確的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...