新聞中心
這篇文章給大家聊一次線上生產系統(tǒng)事故的解決經歷,其背后代表的是線上生產系統(tǒng)的 JVM FullGC 可能引發(fā)的嚴重故障。

業(yè)務場景介紹
先簡單說說線上生產系統(tǒng)的一個背景,因為僅僅是文章作為案例來講,所以弱化大量的業(yè)務背景。
簡單來說,這是一套分布式系統(tǒng),系統(tǒng) A 需要將一個非常核心以及關鍵的數據通過網絡請求,傳輸給另外一個系統(tǒng) B。
這里其實就考慮到了一個問題,如果系統(tǒng) A 剛剛將核心數據傳遞給了系統(tǒng) B,結果系統(tǒng) B 莫名其妙宕機了,豈不是會導致數據丟失?
所以在這個分布式系統(tǒng)的架構設計中,采取了非常經典的一個 Quorum 算法。
這個算法簡單來說,就是系統(tǒng) B 必須要部署奇數個節(jié)點,比如說至少部署 3 臺機器,或者是 5 臺機器,7 臺機器,類似這樣子。
然后系統(tǒng) A 每次傳輸一個數據給系統(tǒng),都必須要對系統(tǒng) B 部署的全部機器都發(fā)送請求,將一份數據傳輸給系統(tǒng)B部署的所有機器。
要判定系統(tǒng) A 對系統(tǒng) B 的一次數據寫是成功的,要求系統(tǒng) A 必須在指定時間范圍內對超過 Quorum 數量的系統(tǒng) B 所在機器傳輸成功。
舉個例子,假設系統(tǒng) B 部署了 3 臺機器,那么他的 Quorum 數量就是:3 / 2 + 1 = 2,也就是說系統(tǒng) B 的 Quorum 數量就是:所有機器數量 / 2 + 1。
所以系統(tǒng) A 要判定一個核心數據是否寫成功,如果系統(tǒng) B 一共部署了 3 臺機器的話,那么系統(tǒng) A 必須在指定時間內收到 2 臺系統(tǒng) B 所在機器返回的寫成功的響應。
此時系統(tǒng) A 才能認為這條數據對系統(tǒng) B 是寫成功了。這個就是所謂的 Quorum 機制。
也就是說,分布式架構下,系統(tǒng)之間傳輸數據,一個系統(tǒng)要確保自己給另外一個系統(tǒng)傳輸的數據不會丟失,必須要在指定時間內,收到另外一個系統(tǒng) Quorum(大多數)數量的機器響應說寫成功。
這套機制實際上在很多分布式系統(tǒng)、中間件系統(tǒng)中都有非常廣泛的使用,我們線上的分布式系統(tǒng)也是采用了這個 Quorum 機制在兩個系統(tǒng)之間傳輸數據。
給大家上一張圖,一起來看一下這套架構長啥樣:
如上圖所示,圖中很清晰的展示了系統(tǒng) A 和系統(tǒng) B 之間傳輸一份數據時的 Quorum 機制。
接下來,我們用代碼給大家展示一下,上面的 Quorum 寫機制在代碼層面大概是什么樣子的。
PS:因為實際這套機制涉及大量的底層網絡傳輸、通信、容錯、優(yōu)化的東西,所以下面代碼經過了大幅度簡化,僅僅表達出了一個核心的意思。
上面就是經過大幅精簡后的代碼,不過核心的意思是表達清晰了。大家可以仔細看兩遍,其實還是很容易弄懂的。
這段代碼含義很簡單,說白了就是異步開啟線程發(fā)送數據給系統(tǒng) B 所有的機器,同時進入一個 while 循環(huán)等待系統(tǒng) B 的 Quorum 數量的機器返回響應結果。
如果超過指定超時時間還沒收到預期數量的機器返回結果,那么就判定系統(tǒng) B 部署的集群出現(xiàn)故障,接著讓系統(tǒng) A 直接退出,相當于系統(tǒng) A 宕機。
整個代碼,就是這么個意思!
問題凸現(xiàn)
光是看代碼其實沒啥難的,但是問題就在于線上運行的時候,可不是跟你寫代碼的時候想的一樣簡單。
有一次線上生產系統(tǒng)運行的過程中,整體系統(tǒng)負載都很平穩(wěn),本來是不應該有什么問題,但是結果突然收到報警,說系統(tǒng) A 突然宕機了。
然后就開始進行排查,左排查右排查,發(fā)現(xiàn)系統(tǒng) B 集群都好好的,不應該有問題。
然后再查查系統(tǒng) A,發(fā)現(xiàn)系統(tǒng) A 別的地方也沒什么問題。***結合系統(tǒng) A 自身的日志,以及系統(tǒng) A 的 JVM FullGC 進行垃圾回收的日志,我們才算是搞清楚了具體的故障原因。
定位問題
其實原因非常的簡單,就是系統(tǒng) A 在線上運行一段時間后,會偶發(fā)性的進行長時間 Stop the World 的 JVM FullGC,也就是大面積垃圾回收。
但是,此時會造成系統(tǒng) A 內部的工作線程大量的卡頓,不再工作。要等 JVM FullGC 結束之后,工作線程才會恢復運作。
我們來看下面那個代碼片段:
但是這種系統(tǒng) A 的莫名宕機是不正確的,因為如果沒有 JVM FullGC,本來上面那個 if 語句是不會成立的。
它會停頓 1 秒鐘進入下一輪 while 循環(huán),接著就可以收到系統(tǒng) B 返回的 Quorum 數量的結果,這個 while 循環(huán)就可以中斷,繼續(xù)運行了。
結果因為出現(xiàn)了 JVM FullGC 卡頓了幾十秒,導致莫名其妙就觸發(fā)了 if 判斷的執(zhí)行,系統(tǒng) A 莫名其妙就退出宕機了。
所以,線上的 JVM FullGC 導致的系統(tǒng)長時間卡頓,真是造成系統(tǒng)不穩(wěn)定運行的隱形殺手之一啊!
解決問題
至于上述代碼穩(wěn)定性的優(yōu)化,也很簡單。我們只要在代碼里加入一些東西,監(jiān)控一下上述代碼中是否發(fā)生了 JVM FullGC。
如果發(fā)生了 JVM FullGC,就自動延長 expireTime 就可以了。
比如下面代碼的改進:
通過上述代碼的改進,就可以有效的優(yōu)化線上系統(tǒng)的穩(wěn)定性,保證其在 JVM FullGC 發(fā)生的情況下,也不會隨意出現(xiàn)異常宕機退出的情況了。
分享名稱:翻車現(xiàn)場:一次JVMFullGC引發(fā)的宕機事故
轉載來于:http://m.fisionsoft.com.cn/article/dpggced.html


咨詢
建站咨詢
