java—Tomcat高性能調優方案詳解

編程語言 Tomcat Java Apache java飛虎隊 java飛虎隊 2017-10-07

一丶原理

概要

Tomcat大致分為兩個部分,Connector組件及Container組件。Connector組件負責控制入口連接,並關聯著一個Executor。Container負責Servlet容器的實現,Executor負責具體的業務邏輯,如Servlet的執行。一個請求到達服務器後,經過以下關鍵幾步,參見圖1:

java—Tomcat高性能調優方案詳解

圖一

  1. OS與客戶端握手並建立連接,並將建立的連接放入完成隊列,不妨叫Acceptor Queque。這個隊列的長度就是Connector的acceptCount值。

  2. Tomcat中的acceptor線程,不斷從Acceptor Queque中獲取連接。

  3. Acceptor Queque隊列中沒有連接,Acceptor線程繼續監視

  4. Acceptor Queque隊列中有新連接,Acceptor線程將檢查當前的連接數是否超過了maxConnections

  5. 如果超過maxConnections,則阻塞。直到連接數小於maxConnections,acceptor線程將請求交由Executor負責執行。

  6. Executor將分配worker線程來處理請求數據的讀取,處理(servlet的執行)以及響應。

參數

  1. acceptCount

    acceptCount 實際上是Bind Socket時候傳遞的backlog值,在linux平臺下含義是已經建立連接還沒有被應用獲取的連接隊列最大長度。此時,如果請求個數達到了acceptCount,新進的請求將拋出refuse connection.

  2. maxConnections:

    顧名思義,即Tomcat允許的同時存在的最大連接數。默認BIO模式下,這個數值等於maxThreads,即最大線程數。超過這個值後,acceptor線程被Block。新進入的連接將由acceptCount控制。噹噹前連接數小於這個數值時,acceptor線程被喚醒,新的連接才能進入Executor執行。

  3. executor

    JDK缺省的ThreadPoolExecutor的邏輯是:

  1. 如果線程數小於coreThreadSize, 優先建立新線程。

  2. 如果線程數大於coreThreadSize小於maxThreadSize,將請求入隊列等待。

  3. 隊列滿的時候,才擴充線程數直到maxThreadSize.

  4. 當隊列滿,同時線程數大於maxThreadSize時,拋出異常。

    Tomcat對JDK的ThreadPoolExecutor默認行為做了修改。使用繼承自無界隊列的LinkedBlockingQueue的TaskQueue,並改寫了其入隊策略,是的tomcat的Executor具有以下特性。

  5. 只要線程不足,並且小於maxThreads, 優先建立新線程。而不是超過coreSize,先入隊列。

  6. Executor如果發生reject,則將任務繼續加入隊列。而默認隊列的size是Integer.MAX_VALUE,因此不會Reject.

    在配置中,Executor可以單獨配置,並被整個Tomcat共享。如果不配置,則Connector組件將使用自己默認的實現。

  7. maxThreads

    Worker線程的最大值。每當一個請求進來時,如果當前線程數小於這個值,則創建新的線程。如果大於這個值,則查找空閒線程。如果沒有空閒線程,則被Block住。

  8. protocol

    可以有三個取值,BIO,NIO, APR。BIO使用阻塞Socket實現,一個連接一個線程的模型。NIO使用的時候非阻塞socket實現。APR是apache實現的一個誇平臺的庫,也是apache http服務以來的核心網絡組件。

    以上介紹的幾個參數之間存在著微妙的關係。一方面可以限制過多的請求來保護系統資源,另一方面提供緩衝隊列來提高系統的吞吐量。在低併發條件下,默認值基本滿足要求。而在高併發的情況下,就需要根據具體的計算資源,評估以上參數的設置,來充分調動計算資源。

    二丶應用

    默認值的效果

    背景中的case除了使用異步Servlet,後端依賴的服務也全部採用異步調用。即在tomcat的woker線程中,沒有任何阻塞,只是做純粹的本地CPU計算, 為了模擬服務失敗的情況,後端服務被mock住,並隨機sleep 0~3s。初次使用了tomcat的默認設置(具體值參見後表中第1條)。由於後端woker線程很快(發送異步請求後就結束了),一個線程在1s內可以處理多於一個的請求。此時,如果maxConnections等於maxThreads 的值,很顯然不能完全激活全部工作線程。如圖2:

    java—Tomcat高性能調優方案詳解

    圖二

    300個線程,只能達到140左右的吞吐量,tomcat worker線程只有17個上下,平均響應時間已經2s多了(理論上正常平均應該1.5s),繼續增加線程,返回異常,說明已經達到了極限值。可以理解為,在異步情況下,20個worker線程每秒就能處理140個請求。

    maxConnection的應用

    為了將worker線程打滿,同時對後端的異步服務有足夠的信心,逐步將maxConnection調整到2000,使用2000個併發打壓。此時200個worker線程都工作了。吞吐量已經達到了1308(如圖3),此時應該是應用最大吞吐量了。至此,初步達到了預期效果。

    java—Tomcat高性能調優方案詳解

    圖三

    NIO Connector的應用

    還有個NIO Connector,看到名字就是支持異步的IO了,在其它參數不變的情況下,換成NIO Connector。如圖4所示,在吞吐量基本不變的情況下,線程數基本減少了一半。

    java—Tomcat高性能調優方案詳解

    圖四

    在啟用NIO Connector,servlet及後端調用不異步的話,如圖所示5,顯然吞吐量上不去,而且還有所下降。

    java—Tomcat高性能調優方案詳解

    圖五

    java—Tomcat高性能調優方案詳解

    圖六

    為了充分發揮tomcat的潛能,需要綜合評估這幾個重要參數。當worker線程處理足夠快的時候,可以適當提高maxConnections值,以便更多的請求得到處理。

    反之,如果後端線程處理較慢,則可考慮減少maxConnections及QueueSize,避免請求堆積而造成請求超時。另外NIO能更高效處理網絡連接及請求。在全棧異步的情況下,能有效減少Worker線程數。

    總結

    好了到這裡, java—Tomcat高性能調優方案詳解就結束了,,不足之處還望大家多多包涵!!覺得收穫的話可以點個關注收藏轉發一波喔,謝謝大佬們支持。(吹一波,233~~)

    下面和大家交流幾點編程的經驗:

    1、多寫多敲代碼,好的代碼與紮實的基礎知識一定是實踐出來的

    2丶 測試、測試再測試,如果你不徹底測試自己的代碼,那恐怕你開發的就不只是代碼,可能還會聲名狼藉。

    3丶 簡化算法,代碼如惡魔,在你完成編碼後,應回頭並且優化它。從長遠來看,這裡或那裡一些的改進,會讓後來的支持人員更加輕鬆。

    4、可以去騰訊課堂的圖靈學院學習一下java架構實戰案例,還挺不錯的。

    最後,每一位讀到這裡的網友,感謝你們能耐心地看完。希望在成為一名更優秀的Java程序員的道路上,我們可以一起學習、一起進步。

    java—Tomcat高性能調優方案詳解

    相關推薦

    推薦中...