大家好,我是樹哥!
對於提供介面服務的應用來說,很多都是用 SpringBoot 預設的 Servlet 容器 Tomcat。在一開始上線的時候,由於大多數流量較小,我們也並不會為 Tomcat 做專門的引數調整。但隨著流量越來越大,應用的各項效能指標越來越差,此時我們大多數都會選擇擴容。
除了擴容之外,我們還可以選擇對 Tomcat 進行效能調優,從而在不增加成本的情況下提升效能。如果面試官問你,流量突增你們一般怎麼做,你只會答擴容可就太差勁了。今天樹哥就跟大家簡單分享下,如何對 Tomcat 進行簡單地效能調優,從而提升應用的效能!
要對 Tomcat 進行效能調優,我們需要先了解其元件架構。Tomcat 的元件架構如下圖所示:
從上圖可以看到,Tomcat 將其業務抽象成了 Server、Service、Connector、Container 等等元件,每個元件都有不同的作用。
到這裡,Tomcat 的核心元件基本上講完了。實際上 Container 元件裡還細分了很多元件,其實對業務的抽象,感興趣的可以繼續看看。
可以看到,Host 是虛擬主機的抽象,Context 是應用程式的抽象,Wrapper 是 Servlet 的抽象,而 Engine 則是處理層的抽象。
在瞭解核心引數之前,我們我們需要大致瞭解一下 Tomcat 對於請求的處理流程。Tomcat 對請求的處理流程如下所示:
上述過程可以用如下示意圖來表示:
在上面的示意圖中有三個非常關鍵的核心引數,這幾個關鍵的引數也是效能調優的關鍵,它們分別是:
從上面三個引數的含義我們可以知道如下幾點結論:
明白這三個核心引數的含義是非常重要的,不然沒有辦法進行後續的效能調優工作。
我們知道 maxThreads 指的是請求處理執行緒的最大數量,在 Tomcat7 和 Tomcat8 中都是預設 200 個。
對於這個引數的設定,需要根據任務的執行內容去調整,一般來說計算公式為:最大執行緒數 = ((IO時間 + CPU時間)/CPU時間) * CPU 核數
。這個公式的思路其實很簡單,就是最大化利用 CPU 的資源。一個任務的耗時分為 IO 耗時和 CPU 耗時,基本上 IO 耗時是最多的,這時候 CPU 是沒事幹的。
因此如果可以讓 CPU 在任務等待 IO 的時候處理其他任務,那麼 CPU 利用率不就上來了麼。一般來說,由於 IO 耗時遠大於 CPU 耗時,因此根據公式計算出來的 maxThreads 數都會遠大於 CPU 核數,這是很正常的。
要注意的是,這個數值也不是越高越好。因為一旦執行緒數太多了,CPU 需要進行上下文切換,這就消耗了一部分 CPU 資源。因此最好的辦法是用上述公式去計算一個基準值,隨後再進行壓力測試,去調整到一個合理的值。一般來說,如果調高了 maxThreads 的值,但是吞吐量沒有提升或者下降的話,那麼表明可能到達了了瓶頸了。
maxConnections 指的是當執行緒池的執行緒達到最大值,並且都在忙的時候,Connector 中的佇列最多能容納多少個連線。一般來說,我們都要設定一個合理的數值,不能讓其無限制堆積。因為 Tomcat 的處理能力肯定是有限的,到達一定程度肯定就處理不過來了,因此你堆積太多了也沒啥用,反而會造成記憶體堆積,最終導致記憶體溢位 OOM 的發生。
一般來說,一個經驗值是可以設定成為 maxThreads 同樣的大小。 我想這樣也是比較合理的,因為在佇列中的連線最多隻需要等待執行緒處理一個任務的時間即可,不會等待太久,響應時間也不會太長。如果你想縮短響應時間,那麼可以將 maxConnections 調低於 maxThreads 一些,這樣可以降低一些響應時間。但要注意的是,如果降得太低的話,可能就會嚴重降低效能,降低吞吐量。
acceptCount 指的是當 Container 執行緒池達到最大數量且沒有空閒執行緒,同時 Connector 佇列達到最大數量時,作業系統最多能接受的連線數。 當佇列中的個數達到最大值後,進來的請求一律被拒絕,預設值是 100。這可以理解成是作業系統的一種自我保護機制吧,堆積太多無法處理,那就直接拒絕掉,保護自身資源。
這個引數的調優資料比較少,但根據其含義,這個值不建議比 maxConnections 大。 因為在這個佇列中的連線,是需要等待的。如果數值太大,就說明會有很多連線沒有被處理。連線越多,那麼其等待的時間就越長,其響應時間就越慢。如果你想響應時間短一些,或許應該調低一下這個值。
有同學會疑惑,為啥有了 maxConnections 了還要有 acceptCount 呢?這不是重複了麼?其實在 BIO 的時代,這兩個數值基本都是相同的。我猜是因為後面出現了 NIO、AIO 等技術,作業系統可以接受更多的使用者端連線了。於是就可以先讓作業系統先建立連線快取著,隨後 Connnector 直接從作業系統處獲取連線即可,這樣就不需要等待作業系統進行耗時的 TCP 連線了,從而提高了效率。
除了上面這三個引數之外,還有幾個非核心引數,但我覺得還是有些作用的。
今天我們分享了 Tomcat 的核心元件,接著講解了 Tomcat 處理請求過程時的 3 個核心引數及其調優經驗。
對於 maxThreads 引數而言,如果按照公式計算的話,我們需要獲取 IO 時間和 CPU 時間,但實際上這兩個值並不是很好獲取。所以一般情況下,我們可以通過壓測的方式來獲得一個比較合適的 maxThreads。
對於 maxConnections 引數而言,可以設定一個與 maxThreads 相同的值,再根據具體情況進行調整。如果想降低響應時間,那麼可以稍微調低一些,否則可以調高一些。對於 acceptCount 引數而言,其調優邏輯與 maxConnections 類似,可以設定與 maxConnections 相似,再根據對相應時間的要求,做一個微調。
好了,這就是今天的分享了。
如果你喜歡這篇文章,請幫忙點贊轉發告訴我,感謝~