Tomcat高性能調優方案詳解

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

圖一
OS與客戶端握手並建立連接,並將建立的連接放入完成隊列,不妨叫Acceptor Queque。這個隊列的長度就是Connector的acceptCount值。
Tomcat中的acceptor線程,不斷從Acceptor Queque中獲取連接。
Acceptor Queque隊列中沒有連接,Acceptor線程繼續監視
Acceptor Queque隊列中有新連接,Acceptor線程將檢查當前的連接數是否超過了maxConnections
如果超過maxConnections,則阻塞。直到連接數小於maxConnections,acceptor線程將請求交由Executor負責執行。
Executor將分配worker線程來處理請求數據的讀取,處理(servlet的執行)以及響應。
參數
acceptCount
acceptCount 實際上是Bind Socket時候傳遞的backlog值,在linux平臺下含義是已經建立連接還沒有被應用獲取的連接隊列最大長度。此時,如果請求個數達到了acceptCount,新進的請求將拋出refuse connection.
maxConnections:
顧名思義,即Tomcat允許的同時存在的最大連接數。默認BIO模式下,這個數值等於maxThreads,即最大線程數。超過這個值後,acceptor線程被Block。新進入的連接將由acceptCount控制。噹噹前連接數小於這個數值時,acceptor線程被喚醒,新的連接才能進入Executor執行。
executor
JDK缺省的ThreadPoolExecutor的邏輯是:
如果線程數小於coreThreadSize, 優先建立新線程。
如果線程數大於coreThreadSize小於maxThreadSize,將請求入隊列等待。
隊列滿的時候,才擴充線程數直到maxThreadSize.
當隊列滿,同時線程數大於maxThreadSize時,拋出異常。
Tomcat對JDK的ThreadPoolExecutor默認行爲做了修改。使用繼承自***隊列的LinkedBlockingQueue的TaskQueue,並改寫了其入隊策略,是的tomcat的Executor具有以下特性。
只要線程不足,並且小於maxThreads, 優先建立新線程。而不是超過coreSize,先入隊列。
Executor如果發生reject,則將任務繼續加入隊列。而默認隊列的size是Integer.MAX_VALUE,因此不會Reject.
在配置中,Executor可以單獨配置,並被整個Tomcat共享。如果不配置,則Connector組件將使用自己默認的實現。
maxThreads
Worker線程的最大值。每當一個請求進來時,如果當前線程數小於這個值,則創建新的線程。如果大於這個值,則查找空閒線程。如果沒有空閒線程,則被Block住。
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:

圖二
300個線程,只能達到140左右的吞吐量,tomcat worker線程只有17個上下,平均響應時間已經2s多了(理論上正常平均應該1.5s),繼續增加線程,返回異常,說明已經達到了極限值。可以理解爲,在異步情況下,20個worker線程每秒就能處理140個請求。
maxConnection的應用
爲了將worker線程打滿,同時對後端的異步服務有足夠的信心,逐步將maxConnection調整到2000,使用2000個併發打壓。此時200個worker線程都工作了。吞吐量已經達到了1308(如圖3),此時應該是應用最大吞吐量了。至此,初步達到了預期效果。

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

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

圖五

圖六
爲了充分發揮tomcat的潛能,需要綜合評估這幾個重要參數。當worker線程處理足夠快的時候,可以適當提高maxConnections值,以便更多的請求得到處理。
反之,如果後端線程處理較慢,則可考慮減少maxConnections及QueueSize,避免請求堆積而造成請求超時。另外NIO能更高效處理網絡連接及請求。在全棧異步的情況下,能有效減少Worker線程數。

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