服務器線程池的最佳尺寸的標準是:對受限制的依賴資源產生足夠的負載—最大化它們的使用率而不讓其透支。下面的“后退調優”一節有更多調整受限制資源池大小的內容。
基于技術的等待點
基于層次的等待點考慮的是在不同服務器之間傳遞請求,而基于技術的等待點關注的則是在單個服務器中如何通過有效地內部工作來傳遞請求?;趯哟蔚恼{優,類似于IBM的隊列調優,只是調整應用的有效第一步,如果忽略了調優應用服務器的內部工作,則會對應用性能產生巨大的影響。這就類似于調整JDBC連接池以發送最佳數量的負載給數據庫,但是忽略了檢查執行的SQL語句——如果查詢需要連接十個表單,每個表單有一百萬個記錄,則最佳負載可能是兩個連接,但是如果我們優化了查詢,則數據庫可能支持二百個連接。深入研究應用服務器和應用使用的潛在技術,可能存在以下通用的基于技術的等待點:
池對象(比如無狀態session bean或者其他應用放入池的業務對象)
緩存設施
持久化存儲或外部依賴池
通訊基礎設施
垃圾收集
大多數情況下,無狀態session bean池的大小被應用服務器優化,不會是一個明顯的等待點,除非池大小被手工錯誤的配置了。但是也存在一些池對象必須手動配置大小——這些可能成為有效的等待點。當一個應用需要一個池化的資源,它必須從池里獲取一個資源實例,使用它,然后釋放給池。如果池太小,所有的對象實例都在被使用,則請求不得不等待一個實例可用。顯而易見,等待一個池化的資源會增加響應時間,如果越來越多的請求被堵塞在等待池化資源,會導致明顯的性能下降。另一方面,如果池很大,它可能消耗過多內存,對總體JVM的性能產生差的影響。池的最佳大小需要權衡,只能在對池的利用情況做徹底的分析才能決定。
池化對象是無狀態的,這意味著應用從池中得到哪個實例都無所謂——任何實例都行。另一方面,緩存的對象都是有狀態的,也就是說當應用從緩存中請求一個對象時,它的目標是一個特定對象。舉一個很粗糙的類比說明一下區別:考慮人們生活當中兩種常見的活動:超市購物和接孩子放學。在超市中,任何收銀員都可以接待每一位顧客,無論顧客選擇哪位收銀員都可以順利結賬。因此收銀員可以池化。但是在接孩子放學時,每一個父母只想要他們自己的孩子,別的孩子是不行的。因此孩子可以被緩存。
如前面所述,緩存提出了一個新的調優挑戰。簡單說,緩存的目的是在本地內存中存儲對象,使應用可以隨時讀取它們,而不是在需要的時候才獲取他們。一個合適大小的緩存可以對通過遠程調用加載對象的行為提供明顯的性能改善。但是,一個不合適大小的緩存,可能產生明顯的性能阻礙。因為緩存維護有狀態的對象,所以重要的一點是在緩存中存儲最頻繁訪問的對象,同時保留額外的空間來處理非頻繁訪問對象。試想如果緩存太小,請求會怎樣:
請求檢查緩存中是否存在某對象,結果失敗。
請求需要查詢外部資源獲取對象數據。
因為緩存通常維護最頻繁訪問的數據,所以這個新對象需要添加到緩存中(它正在被訪問)。
但是如果緩存滿了,必須利用“最少最近訪問”算法選擇一個對象移除。
如果緩存對象的狀態與外部資源不一致,則緩存對象移除之前必須更新外部資源。
新的對象此刻添加到緩存中。
新的對象最終返回給請求。
這是一個低效的過程,如果大多數請求都要執行這些步驟,那么緩存肯定會降低性能。緩存必須調整到足夠大以最小化緩存的“不命中率”,一次不命中意味著需要執行前面提到的七個步驟,但是也不能太大導致占用太多JVM內存。如果緩存需要非常非常大才能滿足性能需要,那么最好是重新考慮被緩存對象的實質和它們到底是否值得緩存。
類似對象池,外部資源池,比如數據庫連接池,也必須足夠大以滿足請求不會被迫等待池中的一個連接變為可用狀態,但是也不能太大,導致應用浪費外部資源。“后退調優”一節討論了如何決定這些池的最佳大小,但是在本節的上下文中,牢記它們代表了一個明顯的等待點。
調優通訊基礎設施遠遠超出了本文討論的范圍,因為其具體實現因產品不同而存在明顯的區別,這包括諸如MSMQ、MQSeries、TIBCO等主流產品。但是請記住,如果一個應用與某消息服務器交互,它必須經過合適的調優或者它也代表了一個等待點。
最后一個明顯影響JVM性能的等待點是垃圾收集。它不太適用本文中描述的等待點分析過程(檢查一個請求,定位導致該請求等待的技術),但是由于它可以對性能產生顯著的影響,所以把它列在這里。不同的JVM實現和不同的垃圾收集策略決定了垃圾收集如何執行,但是在很多情況下,一次主垃圾收集(或者說標記—清除—壓縮垃圾收集)可能導致整個JVM暫停直到垃圾收集完成。一個顯著提高JVM性能的辦法就是優化垃圾收集。如果想了解更多垃圾收集的信息,請加入GeekCap討論應用基礎設施調優。
后退調優
現在關于基于層次的和基于技術的等待點的一切都介紹完了,最后一步就是優化每一個等待點的配置。這一步有時被稱為“后退調優”,其思想非常簡單:
開放所有基于層次的等待點和外部依賴池——也就是配置它們允許過多的負載經過服務器。
根據應用生成均衡的和具有代表性的服務請求。
定位首先透支的等待點,通常是外部依賴,比如數據庫。
減小配置以控制等待點允許足夠的負載經過外部依賴而不透支。
調整所有其他基于層次的等待點,發送足夠的負載經過服務器,最大化受限制的等待點,但是也不讓請求等待。
允許所有其他請求在業務邏輯層之上等待,比如web服務器端。
此處的原則就是應用應該發送一定數量的負載給它的外部依賴資源以最大化它們的使用率又不導致透支—并且所有其他等待點應該合理配置以發送足夠的負載給這些受限制的等待點。例如,如果數據庫對每一個應用服務器最多支持50個連接(例如,配置池容納40或45個連接)。接下來,如果80個線程產生40個數據庫連接,則應用的線程池應配置為80。最后,web服務器在任意時刻應該發送不超過80個請求給每一個應用服務器。
原文轉自:http://www.infoq.com/cn/articles/Wait-Based-Tuning-Steven-Haines