最近系統出現一個問題,少量Proxy出現故障,群組消息應用伺服器會出現記憶體資源不夠導緻的崩潰的情況。我們對每個worker做了過載的保護,并且對每個worker的記憶體使用做了限制。從抓下來的dump來看,記憶體中的sipc對象占用了過多記憶體導緻的崩潰。sipc請求對象在記憶體中的數量遠遠超過設定的數量,說明過載沒有起到作用。
現場描述:
群消息伺服器會通路多台Proxy給同的使用者下行消息。
壞了兩台proxy。
群組消息應用worker與zookeeper斷掉連接配接,沒有任何請求過來從proxy過來。記憶體占用設定的2.4G,每顆cpu在20%左右,正常地對程序抓dump抓不下來,得加-F參數。kill程序的操作,需要一段時間後,程序才會消失。
事故原因分析:
SipcHost在做過載保護的機制是這樣的:
Sipc請求進來的時候,計數器加1,Sipc應答的時候,計數器減1;
計數器如果到達門檻值,對于新請求,直接傳回server busy;
但是目前上線的很多應答無内容的應用為了快速響應用戶端的請求,都是直接發送應答之後再開始做業務邏輯,群消息就是個典型的例子,先給用戶端reponse,再做群消息的分發。
SipcHost的線程池使用的是 java.util.concurrent.Executors.newFixedThreadPool(int nThreads),而這個線程池使用的是無容量限制的LinkedBlockingQueue。
基于以上三點,群消息在做消息分發的時候由于proxy的問題RPC消息發不下去,進入RPC重試機制;由于群消息快速reponse了,是以群消息的Sipc計數器一直為一個比較小的值,群消息的請求一直會進入到SipcHost的Queue中,最終導緻記憶體被使用完畢,所有的線程被hang死。
經驗總結:
盡量不要使用計數器做過載保護,因為計數器由于應用多使用異步的原因,不能準确判斷事務是否完成,隻能根據應用的response來計數,不是很準确。建議使用有一定容量的queue的線程池,queue滿了直接報busy來做過載保護。