成爲Java GC專家系列(4) ——MaxClients in Apache and its... 原

這是“成爲Java GC專家系列文章”的第四篇。

在第一篇文章 成爲Java GC專家系列(1) ——Java垃圾回收機制 中我們學習了不同GC算法的執行過程,GC如何工作,年輕代及年老代的基本概念,在JDK7中你應該瞭解的5種GC類型以及他們的性能如何。

在第二篇文章 成爲Java GC專家系列(2) ——監控Java垃圾回收 中我們學到了JVM到底是如何執行垃圾回收,我們如何監控GC,以及那些工具可以使得監控過程更高效。

在第三篇文章 成爲Java GC專家系列(3) ——如何優化Java垃圾回收中我們通過實際的例子學到了一些可以優化GC的參數。同時我們講解了如何減少對象被轉移到年老代空間,如何縮短Full GC時間,以及如何設置GC類型及內存空間。

在第四篇文章中,我們將闡述Apache中MaxClients 參數的重要性,以及他如何在GC發生時,顯著地影響整個系統的性能。我將提供幾個例子以方便你理解MaxClients 導致的問題。同時我還會說明如何根據系統的內存情況,設置最佳的MaxClients參數值。

MaxClients對於系統的影響

NHN (譯者注:NHN是作者工作的公司)服務的執行環境中存在一組Throttle value-type參數(譯者注:節流閥參數,用於控制系統負載)。這些參數對於系統來說十分重要。下面我們看一下Apache的 MaxClients 參數在 Full GC 發生時是如何影響系統的。

大部分開發人員都知道在由於GC發生而導致的”停止世界現象(STW) “(詳細請參見Java垃圾回收機制)。尤其是,NHN的Java開發人員經常會遇到由於GC原因導致的Tomcat報錯。由於Java 虛擬機 (JVM)管理着內存,以Java爲基礎的程序無法擺脫GC導致的STW現象。假如在某一個時間,當你正在操作你開發的應用時,GC開始執行。即使TTS錯誤沒有發生,你的服務也會給用戶展現未預期的503錯誤。

服務執行環境

由於架構本身的特點,相比較縱向擴展 而言,Web服務更適合橫向擴展(譯者注:增加服務器的數量,而不是提高件配置)。因此,總體來講,物理設備會根據性能要求被配置成1臺Apache+n臺Tomcat。但是本文假設我們的環境是1臺Apache+一臺Tomcat同時安裝在一臺主機上,如下圖所示。

圖1:本文假設的服務執行環境

僅供參考,本文描述的參數基於Apache 2.2.21 (prefork MPM),Tomcat 6.0.35,CentOS 4.72 (32-bit),jdk 1.6.0_24。系統可用內存2GB,垃圾收集器使用ParallelOldGC,AdaptiveSizePolicy採用默認的設置true,堆內存空間600M。

STW 和HTTP 503

讓我們假設訪問Apache的請求爲 200 req/s且有10個httpd進程在運行,另外我們暫時不考慮每個請求的響應時間。在這種前提下,我們假設由於full GC導致的暫停時間爲1秒。 Full GC 發生的時候 Tomcat 會怎樣?

第一件進入你腦海的事情應該是Tomcat會因爲full GC而停止響應任何請求。在這種情況下,Tomcat暫停響應請求時Apache會發生什麼?

當Tomcat暫停時,請求會以200 req/s的速度不斷的涌入Apache。一般來說,在Full GC發生之前,請求可以快速地被10個或更多的httpd進程處理掉。但是,因爲Tomcat暫停了,httpd進程會被不停地創建以響應新進請求。直到超過httpd.conf 文件中定義 MaxClients 爲止。由於MaxClients默認值爲256,所以請求在GC時仍會以200req/s的速度涌入(譯者注:前文有說GC時間爲1s)。

這時,新創建的httpd線程將如何呢

Httpd進程通過mod_jk 模塊所管理的空閒的AJP連接,將請求轉發給Tomcat。如果沒有空閒連接,他會申請創建新的連接。但是,因爲Tomcat暫停了,創建新連接的請求會被拒絕。因此這些請求會被存儲在backlog隊列中,數量的多少取決於server.xml中關於AJP Connector的設置。一旦請求數量超過backlog隊列的空間限制。Apache就會返回拒絕連接錯誤。並且返回HTTP 503 錯誤給用戶。

在這種假設條件下,默認的backlog隊列空間是100,而請求到達速度是200 req/s。因此,full GC導致的一秒鐘的暫停會使得超過100個請求返回503錯誤。

這樣,當Full GC結束後,backlog隊列中存儲的內容會被Tomcat接受並在通過工作線程處理,線程的最大數量取決於MaxThreads的值(默認200)。

MaxClients 與backlog

在這種情況下,設定哪個參數可以避免返回給用戶503錯誤呢?

首先,我們應該知道backlog的值要夠大,以至於能夠容納所有因爲Full GC導致暫停期間涌入的請求。換句話說太應該不小於200。

那麼,這麼設置之後會不會產生新的問題呢

我們將backlog設置爲200後再重複一下上面的測試過程,得到的結果比之前更加糟糕。系統內存使用量一般情況下爲50%,但是,在發生Full GC時快速增加到100%,同時導致交換內存空間快速增加,更爲嚴重的是導致Full GC的暫停時間從1秒變成了4秒甚至更多,系統在此期間完全宕機,不能響應任何請求。

在第一種情況下,只有100或更多的請求返回503錯誤。但是,當我們把backlog調整到200後,超過500個請求會掛起3秒甚至更多地時間無法得到應答。

上面這個例子可以很好的說明當你沒有完全理解各個設置之間的內在關係時(例如,對於系統的影響),盲目修改系統會導致什麼後果。

那麼,爲什麼會產生這個現象呢

問題的根源在於 MaxClients 參數的特性。

將MaxClients 設置爲一個很大的值本身沒有問題,但最重要的是在設定MaxClients 參數時,你要確保當MaxClients 個httpd進程被同時創建時,內存使用量也不會超過80%。

系統的內存交換參數一般被設定爲60(默認)。因此,當內存使用量超過80%時,就會進行內存交換。

讓我們再來看一下爲什麼這個特性會導致上面那個嚴重的問題。當請求以200 req/s的速度涌向Tomcat時,Tomcat由於full GC暫停了。此時backlog被設置爲200。Apache大約創建100個httpd進程。在這種情況下,一旦內存使用量超過80%,操作系統會激活交換內存區域,並且由於系統認爲JVM的老年代中的對象在很長一段時間內未被使用,而將他們移動到交換區域。

最終的結果是,GC使用了內存交換空間,暫停時間劇增。因此httpd進程數進一步增加。從而導致上面描述的內存使用量達到100%的情況。

這兩個場合的唯一區別就是backlog的值:100 vs.200。爲什麼只在200的情況下發生?

兩者不同的原因在於創建的httpd進程的數量。當backlog設置爲100時並且Full GC發生時,會創建100個請求的連接並保存在backlog隊列中。其他請求得到拒絕連接錯誤信息併發揮503錯誤。因此,總的httpd 進程數量僅僅會略高於100。而當backlog被設置爲200時,200個請求會創建連接,因此。總的httpd進程數會多於200。這樣超過閥值,從而導致內存交換的發生。緊接着,不考慮內存使用量而的設定 MaxClients參數,Full GC導致httpd進程數量暴增,引發內存交換,降低系統性能。

MaxClients參數的計算公式

如果系統的內存使2GB,MaxClients 的值在任何情況下都不應該超過內存的80%(1.6GB),以避免由於內存交換導致的性能下降。換句話說。1.6GB的內存應該共享和分配給Apache,Tomcat以及那些默認被安裝的代理程序。

讓我們假設代理程序被默認安裝在系統,並佔用了200m內存,對於Tomcat堆內存的-Xmx 被設定爲 600m。因此根據top命令的結果,Tomcat會一直佔用725m(Perm Gen + Native Heap Area)。最終Apache可以使用700m內存空間。如下所示。

2:測試系統的top截屏

如上所述,我們將內存設爲700m後MaxClients 應該是多少呢?

這要取決於加載模塊的數量,對於NHN Web服務來說。Apache只是個簡單的代理轉發,每個httpd線程4m內存(根據top命令的結果)足以(參見圖2)。因此。700m內存對應的 MaxClients應該是175。

總結

一個健壯的服務配置至少應該能夠降低在服務過載時宕機的時間,在合理的範圍內成功的應答請求。針對基於Java的Web服務。你必須檢查你的服務在Full GC導致的STW時間內能否穩定的響應請求。

爲了響應更多的用戶請求和應對DDoS攻擊,在沒有全面考慮系統內存等因素的情況下,貿然地將 MaxClients設置爲一個很大的值,那麼它將失去作爲閥值的功能,而導致系統出現更嚴重的問題。

本文提到的情況只會持續3-5秒,因此絕大多數傳統的監控工具都無法及時的發現。


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