程式猿必須知道的關於 Tomcat 的知識點

Supreme_Sir發表於2020-12-11

身為 Java 程式設計師,Tomcat 應該算是我們接觸的最多的 web 容器了。同時,作為企業生產工具的 “八阿哥” 們,平常只顧著埋頭寫 BUG,哪有什麼時間整理用過的知識點。今天,我將和大家一起梳理一下關於 Tomcat 的相關內容,由淺入深,從入門到放棄。奧利給~!

一、HTTP 請求處理流程

在這裡插入圖片描述

如上圖所示:

  1. 使用者通過瀏覽器發起請求。
  2. 瀏覽器向目標伺服器發起 TCP 連線請求。
  3. 經過三次握手後客戶端瀏覽器與目標伺服器成功建立 TCP 連線。(基於 Socket 實現)
  4. 瀏覽器生成 HTTP 格式的資料包。
  5. 瀏覽器傳送 TCP 請求資料包。(這個資料包的請求頭為 TCP 格式的請求頭,上一步封裝的HTTP 格式的資料包被作為 TCP 請求體傳送至伺服器)
  6. 伺服器解析 TCP 請求體中的 HTTP 資料包。
  7. 伺服器處理請求,完成相關業務邏輯。
  8. 伺服器生成 HTTP 格式的響應資料包。
  9. 伺服器將響應資料包傳送至客戶端瀏覽器。
  10. 瀏覽器解析 HTTP 格式的響應資料包。(解析出的資料為靜態資料如:HTMLCSSJS、圖片等)
  11. 瀏覽器渲染響應結果,呈現靜態資料給使用者。

二、Tomcat 總體架構說明

在這裡插入圖片描述

Tomcat 作為 Servlet 容器從軟體執行角度看,主要可以分為兩大模組,一個是 Http 請求接收、響應模組(Coyote),另一個是請求處理模組,即從 Servlet 容器中獲取與請求對應的處理方法並執行(Catalina)。

Coyote 聯結器包含以下兩個模組:

  1. ProtocolHandler 協議處理介面。這個介面通過 EndpointProcessor ,實現針對具體協議的處
    理能⼒。Tomcat 按照協議和 I/O 提供了6個實現類 : AjpNioProtocolAjpAprProtocolAjpNio2ProtocolHttp11NioProtocolHttp11Nio2ProtocolHttp11AprProtocol

    • EndPoint 是用來實現 TCP/IP 協議的,是 Coyote 通訊的端點,即通訊監聽的接⼝。是具體 Socket 接收和傳送處理器。EndPoint 是對傳輸層的抽象。

    • Processor ⽤來實現 HTTP 協議,可將來⾃ EndPointSocket,讀取位元組流解析成 Tomcat 自己定義的 RequestResponse 物件。Processor 是對應⽤層協議的抽象(注意:這裡的 Tomcat RequestResponse 物件,還不是我們在 Servlet 中所用到的 ServletRequestServletResponse 物件。)

  2. Adapter 由於請求協議的不同,客戶端傳送至後臺的請求資訊也不盡相同。為了能夠轉換成統一的 ServletRequestServletResponse 物件方便 Servlet 容器處理業務,則需要該介面的實現類,對傳送至 Tomcat 的請求進行統一的適配處理。進而使呼叫 Servlet 容器中的方法變的簡單。

Servlet 容器又名 Catalina。一個 Catalina 例項只能擁有一個 Server 例項,一個 Server 例項可以包含多個 Service 例項。而每一個 Service 例項又可以包含多個 Connector 和一個 Container。一個 Container 只能有一個 Engine,一個 Engine 可以有多個 Host ,每個 Host 可以包含多個 Context (網站),每個 Context 中可以有多個 WrapperServlet)。

  1. Catalina 例項可以被看成就是一個 Tomcat 例項。該例項用以解析 server.xml 配置檔案, 以此來建立伺服器Server元件並進⾏管理。
  2. Server 表示整個 Catalina Servlet 容器以及其它元件,負責組裝並啟動 Servlaet 引擎,Tomcat 聯結器。
  3. ServiceServer 內部的元件,⼀個 Server 包含多個 Service。它將若⼲個 Connector 元件繫結到⼀個 Container。(通常情況下只用一個 Service 就夠了)
  4. Container 負責處理⽤戶的 servlet 請求,並將處理結果返回給使用者。
    • Engine 是整個 CatalinaServlet 引擎,⽤來管理多個虛擬站點,⼀個 Service 最多隻能有⼀個 Engine,但是⼀個 Engine 可包含多個 Host
    • Host 代表⼀個虛擬主機,或者說⼀個站點,可以給 Tomcat 配置多個虛擬主機地址。⽽⼀個虛擬主機下可包含多個 Context
    • Context 表示⼀個 Web 應⽤程式, ⼀個 Web 應⽤可包含多個 Wrapper
    • Wrapper 表示⼀個 ServletWrapper 作為容器中的最底層,不能包含⼦容器。

Tips

上述元件的配置其實就體現在 Tomcat 軟體中的 conf/server.xml 裡。

三、Tomcat 核心配置檔案 server.xml

問個實在的問題:當你第一次甚至說上一次看 Tomcat 的配置檔案 server.xml 時,你的第一感受是啥?反正我最真切的感受就是:“我靠,英文真多~!”。不過靜下心來再看,更多的都是說明性文字。刪除註釋後再看,就跟被拔光毛的雞一樣,也就那樣。不信你看

<?xml version="1.0" encoding="UTF-8"?>
<!-- port:關閉伺服器的監聽端⼝  shutdown:關閉伺服器的指令字串 -->
<Server port="8005" shutdown="SHUTDOWN">
	 <!-- 以⽇志形式輸出伺服器 、作業系統、JVM的版本資訊 -->
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <!-- 載入(伺服器啟動)和銷燬(伺服器停⽌)APR。如果找不到 APR 庫,則會輸出⽇志,但並不影響 Tomcat 啟動 -->
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <!-- 避免JRE記憶體洩漏問題 -->
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <!-- 載入(伺服器啟動)和銷燬(伺服器停⽌)全域性命名服務 -->
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <!-- 在Context停⽌時重建 Executor 池中的執行緒,以避免 ThreadLocal 相關的記憶體洩漏 -->
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
    <!-- 全域性命名服務,定義伺服器的全域性 JNDI 資源 -->
    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" 
        	description="User database that can be updated and saved" 
        	factory="org.apache.catalina.users.MemoryUserDatabaseFactory" 
            pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>

    <Service name="Catalina">
    	<!-- 處理 HTTP/1.1 協議的請求 -->
        <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
                   redirectPort="8443" />
        <!-- 處理 AJP/1.3 協議的請求 用不到的話可以註釋掉 -->
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
        
        <Engine name="Catalina" defaultHost="localhost">
            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
                       resourceName="UserDatabase" />
            </Realm>
            <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 
                	prefix="localhost_access_log" suffix=".txt" 
                    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
            </Host>
        </Engine>
    </Service>
</Server>

這麼一整理是不是瞬間清爽了很多。需要注意的是,通常情況下,上面配置檔案裡新增了註釋的部分,除了 Connector 標籤中的引數,其他標籤的內容一般不需要修改,保持預設即可。

四、核心配置檔案進階

4-1 <Service> 標籤

<Service name="Catalina">
  ...
</Service>

該標籤⽤於建立 Service 例項,預設使⽤ org.apache.catalina.core.StandardService 物件。預設情況下,Tomcat 僅指定了 Service 的名稱, 值為 “Catalina”。
Service ⼦標籤有 : ListenerExecutorConnectorEngine。其中:

  • Listener ⽤於為 Service 新增⽣命週期監聽器(用的比較少,故下文不做說明)
  • Executor ⽤於配置 Service 共享執行緒池
  • Connector ⽤於配置 Service 包含的連結器
  • Engine ⽤於配置 Service 中連結器對應的 Servlet 容器引擎

4-2 <Executor> 標籤

該標籤用於為 Service 新增執行緒池。預設情況下,在 server.xml 檔案中,該標籤是被註釋掉的,即未開啟執行緒池的。如果想開啟,則開啟註釋,並配置相關屬性即可。

<Executor name="commonThreadPool"
    namePrefix="thread-exec-"
    maxThreads="200"
    minSpareThreads="100"
    maxIdleTime="60000"
    maxQueueSize="Integer.MAX_VALUE"
    prestartminSpareThreads="false" 
    threadPriority="5"
    className="org.apache.catalina.core.StandardThreadExecutor"/>

<Executor> 標籤屬性含義

  • name:執行緒池名稱。通過該屬性的值,可在 <Connector> 標籤中指定對應執行緒池。
  • namePrefix:所建立的每個執行緒的名稱字首,⼀個單獨的執行緒名稱為:namePrefix + threadNumber
  • maxThreads:執行緒池中最⼤執行緒數。
  • minSpareThreads:最小空閒執行緒數,即活躍執行緒數,也就是核⼼池執行緒數。這些執行緒不會被銷燬,會⼀直存在。
  • maxIdleTime:執行緒空閒時間,超過該時間後,空閒執行緒會被銷燬,預設值為 6000(1分鐘),單位毫秒。
  • maxQueueSize:在被執⾏前最⼤執行緒排隊數⽬,預設為 Int 的最⼤值,也就是⼴義的⽆限。除⾮特殊情況,否則這個值不需要更改,不然會有請求不會被處理的情況發⽣。
  • prestartminSpareThreads:啟動執行緒池時是否啟動 minSpareThreads 部分執行緒。預設值為 false,即不啟動。
  • threadPriority:執行緒池中執行緒優先順序,預設值為 5,值從 110
  • className:執行緒池實現類,未指定情況下,預設實現類為org.apache.catalina.core.StandardThreadExecutor。如果想使⽤⾃定義執行緒池⾸先需要實現org.apache.catalina.Executor 接⼝。

4-3 <Connector> 標籤

該⽤於建立連結器例項預設情況下,server.xml 配置了兩個連結器,⼀個⽀持 HTTP 協議,⼀個⽀持 AJP 協議⼤多數情況下,我們並不需要新增連結器配置,只是根據需要對已有連結器進⾏優化。

<Connector port="8080"
          protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443"
          executor="commonThreadPool"
          URIEncoding="UTF-8"
          minSpareThreads="100"
          maxThreads="1000"          
          acceptCount="1000"
          maxConnections="1000"
          compression="on"
          compressionMinSize="2048"
          disableUploadTimeout="true"
           /> 

<Connector> 標籤屬性含義

  • port:端⼝號,Connector ⽤於建立服務端 Socket 並進⾏監聽, 以等待客戶端請求連結。如果該屬性設定為 0Tomcat 將會隨機選擇⼀個可⽤的端⼝號給當前 Connector 使⽤。
  • protocol:當前 Connector ⽀持的訪問協議。 預設為 HTTP/1.1,並採⽤⾃動切換機制選擇⼀個基於JAVA NIO 的連結器或者基於本地 APR 的連結器。(若想使用 APR 提高 Tomcat 的併發效能,則需要安裝相關執行庫後,再進行配置修改)
  • connectionTimeOutConnector 接收連結後的等待超時時間, 單位為 毫秒。 -1 表示不超時。
  • redirectPort:當前 Connector 不⽀持 SSL 請求。如果接收到了⼀個請求, 且同時符合 security-constraint 約束,需要 SSL 傳輸,Catalina 則會⾃動將請求重定向到指定的端⼝。
  • executor:指定要使用的共享執行緒池的名稱。
  • URIEncoding:⽤於指定編碼 URI 的字元編碼,Tomcat8.x 版本預設的編碼為 UTF-8, Tomcat7.x版本預設為 ISO-8859-1
  • 當我們不使用共享執行緒池(即不新增 executor 屬性),選擇讓每一個 Connector 單獨維護自己的執行緒池時可通過 maxThreadsminSpareThreadsacceptCountmaxConnections 進行 Connector 執行緒池的配置。需要注意的是 maxThreadsacceptCountmaxConnections 這三個屬性的值通常保持一致。
  • compression:是否開啟資料請求等的 gzip 格式壓縮。
  • compressionMinSize:最小壓縮大小,即當資料請求超過指定值後才會進行壓縮。
  • disableUploadTimeout:允許 Servlet 容器,使用較長的連線超時值,以使 Servlet 有較長的時間來完成它的執行,預設值為 false

4-4 <Engine> 標籤

<Engine name="Catalina" defaultHost="localhost">
  ...
</Engine>

該標籤表示 Servlet 引擎。其屬性 name ⽤於指定 Engine 的名稱, 預設為CatalinadefaultHost 屬性用於指定預設使⽤的虛擬主機名稱,當客戶端請求指向的主機⽆效時,將交由預設的虛擬主機處理,預設為值 localhost

4-5 <Host> 標籤

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

該標籤用於配置⼀個虛擬主機。

  • name:指定虛擬主機的名稱。
  • appBase:指定引用基礎目錄。
  • unpackWARs:是否自動解壓 war 包。
  • autoDeploy:當資源有變更時是否自動部署。

4-6 <Context> 標籤

<Host name="www.abc.com"  appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Context docBase="/Users/yingdian/web_demo" path="/web_demo"></Context>  
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

<Host> 標籤中的 <Context> 標籤用於配置一個 Web 應用。

  • docBase:指定 Web 應⽤⽬錄或者 War 包的部署路徑。可以是絕對路徑,也可以是相對於 Host appBase 的相對路徑。(appBase 的子目錄)
  • pathWeb 應⽤的 URL 訪問路徑。如上所示,我們 Host 名為 www.abc.com, 則該 Web 應⽤訪問的根路徑為:http://www.abc.com:8080/web_demo

五、Tomcat 配置 HTTPS

  1. 使⽤ JDK 中的 keytool ⼯具⽣成免費的祕鑰庫⽂件(證照)。

    keytool -genkey -alias SupremeSir -keyalg RSA -keystore supreme.keystore 
    

在這裡插入圖片描述

  1. 配置 server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" schema="https" secure="true" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="E:\**\apache-tomcat\bin\supreme.keystore" 					     certificateKeystorePassword="123456"  type="RSA"/>
    </SSLHostConfig>
</Connector>

certificateKeystoreFile:指定證照所在位置。

certificateKeystorePassword:指定證照祕鑰庫口令。

六、Tomcat 效能優化

Tomcat 優化沒有明確的引數值可以直接去使⽤,必須根據⾃⼰的真實⽣產環境來進⾏調整,調優是⼀個過程。調優主要從兩個⽅⾯進⾏:

  1. JVM 虛擬機器優化(優化記憶體模型)
  2. Tomcat ⾃身配置的優化

6-1 Java 虛擬機器記憶體相關引數

引數引數作用優化建議
-server啟動Server,以服務端模式運⾏服務端模式建議開啟
-Xms最⼩堆記憶體建議與-Xmx設定相同
-Xmx最⼤堆記憶體建議設定為可⽤記憶體的80%
-XX:MetaspaceSize元空間初始值
-XX:MaxMetaspaceSize元空間最⼤記憶體預設⽆限
-XX:NewRatio年輕代和⽼年代⼤⼩⽐值,取值為整數,預設為2不需要修改
-XX:SurvivorRatioEden區與Survivor區⼤⼩的⽐值,取值為整數,預設為8不需要修改

bin/catalina.sh 的指令碼中 , 追加如下配置

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

6-2 垃圾收集器

  1. 串⾏收集器(Serial Collector

    單執行緒執⾏所有的垃圾回收⼯作, 適⽤於單核CPU伺服器。

  2. 並⾏收集器(Parallel Collector

    ⼜稱為吞吐量收集器(關注吞吐量), 以並⾏的⽅式執⾏年輕代的垃圾回收, 該⽅式可以顯著降
    低垃圾回收的開銷(指多條垃圾收集執行緒並⾏⼯作,但此時⽤戶執行緒仍然處於等待狀態)。適⽤於多
    處理器或多執行緒硬體上運⾏的資料量較⼤的應⽤。

  3. 併發收集器(Concurrent Collector

    以併發的⽅式執⾏⼤部分垃圾回收⼯作,以縮短垃圾回收的暫停時間。適⽤於那些響應時間優先於
    吞吐量的應⽤, 因為該收集器雖然最⼩化了暫停時間(指⽤戶執行緒與垃圾收集執行緒同時執⾏,但不⼀
    定是並⾏的,可能會交替進⾏), 但是會降低應⽤程式的效能。

  4. CMS 收集器(Concurrent Mark Sweep Collector

    併發標記清除收集器, 適⽤於那些更願意縮短垃圾回收暫停時間並且負擔的起與垃圾回收共享處
    理器資源的應⽤

  5. G1 收集器(Garbage-First Garbage Collector

    適⽤於⼤容量記憶體的多核伺服器, 可以在滿⾜垃圾回收暫停時間⽬標的同時, 以最⼤可能性實現
    ⾼吞吐量(JDK1.7 之後)

    垃圾回收器引數

    引數描述
    -XX:+UseSerialGC啟⽤串⾏收集器
    -XX:+UseParallelGC啟⽤並⾏垃圾收集器,配置了該選項,那麼 -XX:+UseParallelOldGC預設啟⽤
    -XX:+UseParNewGC年輕代採⽤並⾏收集器,如果設定了 -XX:+UseConcMarkSweepGC選項,⾃動啟⽤
    -XX:ParallelGCThreads年輕代及⽼年代垃圾回收使⽤的執行緒數。預設值依賴於JVM使⽤的CPU個數
    -XX:+UseConcMarkSweepGC(CMS)對於⽼年代,啟⽤CMS垃圾收集器。 當並⾏收集器⽆法滿⾜應⽤的延遲需求是,推薦使⽤CMS或G1收集器。啟⽤該選項後,-XX:+UseParNewGC⾃動啟⽤。
    -XX:+UseG1GC啟⽤G1收集器。 G1是伺服器型別的收集器, ⽤於多核、⼤記憶體的機器。它在保持⾼吞吐量的情況下,⾼概率滿⾜GC暫停時間的⽬標。

    bin/catalina.sh 的指令碼中 , 追加如下配置:

    JAVA_OPTS="-XX:+UseConcMarkSweepGC"
    

    如果存在虛擬機器記憶體相關引數配置,則直接在 JAVA_OPTS 中追加即可。

6-3 Tomcat 配置調優

  1. 啟用 Tomcat 執行緒池

  2. 調整 Tomcat 的聯結器

    調整 Tomcat/conf/server.xml 中關於連結器的配置可以提升應⽤伺服器的效能。

    引數說明
    maxConnections最⼤連線數,當到達該值後,伺服器接收但不會處理更多的請求, 額外的請求將會阻塞直到連線數低於maxConnections 。可通過ulimit -a 檢視伺服器限制。對於CPU要求更⾼(計算密集型)時,建議不要配置過⼤ ; 對於CPU要求不是特別⾼時,建議配置在2000左右(受伺服器效能影響)。 當然這個需要伺服器硬體的⽀持
    maxThreads最⼤執行緒數,需要根據伺服器的硬體情況,進⾏⼀個合理的設定
    acceptCount最⼤排隊等待數,當伺服器接收的請求數量到達maxConnections ,此時Tomcat會將後⾯的請求,存放在任務佇列中進⾏排序, acceptCount指的就是任務佇列中排隊等待的請求數 。 ⼀臺Tomcat的最⼤的請求處理數量,是maxConnections+acceptCount
  3. 禁⽤ AJP 聯結器
    在這裡插入圖片描述

  4. 調整 IO 模式

    Tomcat8 之前的版本預設使⽤ BIO(阻塞式 IO),對於每⼀個請求都要建立⼀個執行緒來處理,不適合⾼併發;Tomcat8 以後的版本預設使⽤ NIO 模式(⾮阻塞式 IO)。

    在這裡插入圖片描述
    Tomcat 併發效能有較⾼要求或者出現瓶頸時,我們可以嘗試使⽤ APR 模式,APRApache Portable Runtime)是從作業系統級別解決非同步 IO 問題,使⽤時需要在作業系統上安裝 APRNative(因為 APR 原理是使⽤使⽤ JNI 技術調⽤作業系統底層的 IO 接⼝)。

  5. 動靜分離

    可以使⽤ Nginx + Tomcat 相結合的部署⽅案,Nginx 負責靜態資源訪問,Tomcat 負責 Jsp 等動態資
    源訪問處理(因為 Tomcat 不擅⻓處理靜態資源)。

原始碼

原始碼下載(暫無)

------------------------------------- 保持熱愛,翻閱山海。 -------------------------------------

相關文章