接口流量突增,如何做好性能調優?

大家好,我是樹哥!

對於提供接口服務的應用來說,很多都是用 SpringBoot 默認的 Servlet 容器 Tomcat。在一開始上線的時候,由於大多數流量較小,我們也並不會爲 Tomcat 做專門的參數調整。但隨着流量越來越大,應用的各項性能指標越來越差,此時我們大多數都會選擇擴容。

除了擴容之外,我們還可以選擇對 Tomcat 進行性能調優,從而在不增加成本的情況下提升性能。如果面試官問你,流量突增你們一般怎麼做,你只會答擴容可就太差勁了。今天樹哥就跟大家簡單分享下,如何對 Tomcat 進行簡單地性能調優,從而提升應用的性能!

組件架構

要對 Tomcat 進行性能調優,我們需要先了解其組件架構。Tomcat 的組件架構如下圖所示:

Tomcat組件結構示意圖

從上圖可以看到,Tomcat 將其業務抽象成了 Server、Service、Connector、Container 等等組件,每個組件都有不同的作用。

  • Server 組件。 Server 組件是 Tomcat 最外層的組件,該組件是 Tomcat 實例本身的抽象,代表着 Tomcat 自身。一個 Server 組件可以有一個或多個 Service 組件。
  • Service 組件。 Service 組件是 Tomcat 中一組提供服務、處理請求的組件,一個 Service 組件可以有多個 Connector 連接器和一個 Container,有多個 Connector 表示其可以同時使用多種協議接收用戶請求。
  • Connector 組件。 Connector 負責處理客戶端的連接,它提供各種服務協議支持,包括:BIO、NIO、AIO 等等。其存在的價值在於,爲 Container 容器屏蔽了多協議的複雜性,統一了 Container 容器的處理標準。
  • Container 組件。 Container 組件是負責具體業務邏輯處理的容器,當 Connector 組件與客戶端建立連接後,便會將請求轉發給 Container 組件的 Engine 組件處理。

到這裏,Tomcat 的核心組件基本上講完了。實際上 Container 組件裏還細分了很多組件,其實對業務的抽象,感興趣的可以繼續看看。

  • Engine 組件。 Engine 組件表示可運行的 Servlet 實例,包含了 Servlet 容器的核心功能,其可以有一個或多個虛擬主機(Host)。其主要功能是將請求委託給合適的虛擬主機處理,即根據 URL 路徑的配置匹配到合適的虛擬主機處理。
  • Host 組件。 Host 組件負責運行多個應用,其負責安裝這些應用,其主要作用是解析 web.xml 文件,並將其匹配到對應的 Context 組件。
  • Context 組件。 Context 組件代表具體的 Web 應用程序本身,其最重要的功能就是管理裏面的 Servlet 實例。一個 Context 可以有一個或者多個 Servlet 實例。
  • Wrapper 組件。 一個 Wrapper 組件代表一個 Servlet,它負責管理一個 Servlet,包括 Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器。

可以看到,Host 是虛擬主機的抽象,Context 是應用程序的抽象,Wrapper 是 Servlet 的抽象,而 Engine 則是處理層的抽象。

核心參數

在瞭解核心參數之前,我們我們需要大致瞭解一下 Tomcat 對於請求的處理流程。Tomcat 對請求的處理流程如下所示:

  • 首先,客戶端向 Tomcat 服務器發起請求,Connector 組件監聽到請求,於是與客戶端建立起連接。
  • 接着,Connector 將請求封裝後轉發給 Engine 組件處理。
  • 最後,Engine 組件處理完之後將結果返回給 Connector,Connector 組件再將結果返回給客戶端。

上述過程可以用如下示意圖來表示:

Tomcat核心參數示意圖

在上面的示意圖中有三個非常關鍵的核心參數,這幾個關鍵的參數也是性能調優的關鍵,它們分別是:

  1. acceptCount:當 Container 線程池達到最大數量且沒有空閒線程,同時 Connector 隊列達到最大數量時,操作系統最多能接受的連接數。
  2. maxConnections:當 Container 線程池達到最大數量且沒有空閒線程時,Connector 的隊列能接收的最大線程數。
  3. maxThreads: Container 線程池的處理線程的最大數量。

從上面三個參數的含義我們可以知道如下幾點結論:

  1. 客戶端並不是直接與 Tomcat 的 Connector 組件建立聯繫的,而是先與操作系統建立,然後再移交給 Connector 的。這點很重要,不然你就無法理解 acceptCount 這個參數。
  2. 不僅僅 Connector 組件中有隊列,操作系統中也有隊列來臨時存儲與客戶端的連接,這也是很關鍵的點。
  3. 我們所說的線程池,指的是 Container 這個容器裏的線程池。

明白這三個核心參數的含義是非常重要的,不然沒有辦法進行後續的性能調優工作。

maxThreads

我們知道 maxThreads 指的是請求處理線程的最大數量,在 Tomcat7 和 Tomcat8 中都是默認 200 個。

對於這個參數的設置,需要根據任務的執行內容去調整,一般來說計算公式爲:最大線程數 = ((IO時間 + CPU時間)/CPU時間) * CPU 核數。這個公式的思路其實很簡單,就是最大化利用 CPU 的資源。一個任務的耗時分爲 IO 耗時和 CPU 耗時,基本上 IO 耗時是最多的,這時候 CPU 是沒事幹的。

因此如果可以讓 CPU 在任務等待 IO 的時候處理其他任務,那麼 CPU 利用率不就上來了麼。一般來說,由於 IO 耗時遠大於 CPU 耗時,因此根據公式計算出來的 maxThreads 數都會遠大於 CPU 核數,這是很正常的。

要注意的是,這個數值也不是越高越好。因爲一旦線程數太多了,CPU 需要進行上下文切換,這就消耗了一部分 CPU 資源。因此最好的辦法是用上述公式去計算一個基準值,隨後再進行壓力測試,去調整到一個合理的值。一般來說,如果調高了 maxThreads 的值,但是吞吐量沒有提升或者下降的話,那麼表明可能到達了了瓶頸了。

maxConnections

maxConnections 指的是當線程池的線程達到最大值,並且都在忙的時候,Connector 中的隊列最多能容納多少個連接。一般來說,我們都要設置一個合理的數值,不能讓其無限制堆積。因爲 Tomcat 的處理能力肯定是有限的,到達一定程度肯定就處理不過來了,因此你堆積太多了也沒啥用,反而會造成內存堆積,最終導致內存溢出 OOM 的發生。

一般來說,一個經驗值是可以設置成爲 maxThreads 同樣的大小。 我想這樣也是比較合理的,因爲在隊列中的連接最多隻需要等待線程處理一個任務的時間即可,不會等待太久,響應時間也不會太長。如果你想縮短響應時間,那麼可以將 maxConnections 調低於 maxThreads 一些,這樣可以降低一些響應時間。但要注意的是,如果降得太低的話,可能就會嚴重降低性能,降低吞吐量。

acceptCount

acceptCount 指的是當 Container 線程池達到最大數量且沒有空閒線程,同時 Connector 隊列達到最大數量時,操作系統最多能接受的連接數。 當隊列中的個數達到最大值後,進來的請求一律被拒絕,默認值是 100。這可以理解成是操作系統的一種自我保護機制吧,堆積太多無法處理,那就直接拒絕掉,保護自身資源。

這個參數的調優資料比較少,但根據其含義,這個值不建議比 maxConnections 大。 因爲在這個隊列中的連接,是需要等待的。如果數值太大,就說明會有很多連接沒有被處理。連接越多,那麼其等待的時間就越長,其響應時間就越慢。如果你想響應時間短一些,或許應該調低一下這個值。

有同學會疑惑,爲啥有了 maxConnections 了還要有 acceptCount 呢?這不是重複了麼?其實在 BIO 的時代,這兩個數值基本都是相同的。我猜是因爲後面出現了 NIO、AIO 等技術,操作系統可以接受更多的客戶端連接了。於是就可以先讓操作系統先建立連接緩存着,隨後 Connnector 直接從操作系統處獲取連接即可,這樣就不需要等待操作系統進行耗時的 TCP 連接了,從而提高了效率。

除了上面這三個參數之外,還有幾個非核心參數,但我覺得還是有些作用的。

  • connectionTimeout 參數, 表示建立連接後的等待超時時間,如果超過這個時間,那麼就會直接返回超時。
  • minSpareThreads 參數, 表示最小存活線程數,也就是如果沒有請求了,那麼最低要保持幾個線程存活。這個參數與是否有突發流程相關聯,在有突發流量的情況下,如果這個數值太低,那麼就會導致瞬時的響應時間比較長。

總結

今天我們分享了 Tomcat 的核心組件,接着講解了 Tomcat 處理請求過程時的 3 個核心參數及其調優經驗。

對於 maxThreads 參數而言,如果按照公式計算的話,我們需要獲取 IO 時間和 CPU 時間,但實際上這兩個值並不是很好獲取。所以一般情況下,我們可以通過壓測的方式來獲得一個比較合適的 maxThreads。

對於 maxConnections 參數而言,可以設置一個與 maxThreads 相同的值,再根據具體情況進行調整。如果想降低響應時間,那麼可以稍微調低一些,否則可以調高一些。對於 acceptCount 參數而言,其調優邏輯與 maxConnections 類似,可以設置與 maxConnections 相似,再根據對相應時間的要求,做一個微調。

好了,這就是今天的分享了。

如果你喜歡這篇文章,請幫忙點贊轉發告訴我,感謝~

Tomcat 性能優化

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章