歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100)
週一至週五早8點半!精品技術文章準時送上!
“ 上篇文章一次 JVM FullGC的背後,竟隱藏著驚心動魄的線上生產事故!,給大家講了一個線上系統因為JVM FullGC異常當機的case。
這篇文章,我們繼續給大家聊聊另外一個線上系統在生產環境遇到的問題。
一、背景介紹
背景情況是這樣:線上一個系統,在某次高峰期間MQ中介軟體故障的情況下,觸發了降級機制,結果降級機制觸發之後執行了一小會兒,突然系統就完全卡死,無法響應任何請求。
給大家簡單介紹一下這個系統的整體架構,這個系統簡單來說就是有一個非常核心的行為,就是往MQ裡寫入資料,但是這個往MQ裡寫入的資料是非常核心及關鍵的,絕對不容許有丟失。
所以最初就設計了一個降級機制,如果一旦MQ中介軟體故障,那麼這個系統立馬就會把核心資料寫入本地磁碟檔案。
額外提一句,如果有同學不太清楚MQ中介軟體的概念,建議看一下之前發的一篇文章「Java進階面試系列之一」你們系統架構中為何要引入訊息中介軟體?,先對MQ中介軟體這個東西做一個基本的瞭解。
但是如果說在高峰期併發量比較高的情況下,接收到一條資料立馬同步寫本地磁碟檔案,這個效能絕對是極其差的,會導致系統自身的吞吐量瞬間大幅度下降,這個降級機制是絕對無法在生產環境執行的,因為自己就會被高併發請求壓垮。
因此當時設計的時候,對降級機制進行了一番精心的設計。
我們的核心思路是一旦MQ中介軟體故障,觸發降級機制之後,系統接收到一條請求不是立馬寫本地磁碟,而是採用記憶體雙緩衝 + 批量刷磁碟的機制。
簡單來說,系統接收到一條訊息就會立馬寫記憶體緩衝,然後開啟一個後臺執行緒把記憶體緩衝的資料重新整理到磁碟上去。
整個過程,大家看看下面的圖,就知道了。
這個記憶體緩衝實際在設計的時候,分為了兩個區域。
一個是current區域,用來供系統寫入資料,另外一個是ready區域,用來供後臺執行緒重新整理資料到磁碟裡去。
每一塊記憶體區域設定的緩衝大小是512kb,系統接收到請求就寫current緩衝區,但是current緩衝區總共就512kb的記憶體空間,因此一定會寫滿。
同樣,大家結合下面的圖,一起來看看。
current緩衝區寫滿之後,就會交換current緩衝區和ready緩衝區。交換過後,ready緩衝區承載了之前寫滿的512kb的資料。
然後current緩衝區此時是空的,可以繼續接著系統繼續將新來的資料寫入交換後的新的current緩衝區。
整個過程如下圖所示:
此時,後臺執行緒就可以將ready緩衝區中的資料通過Java NIO的API,直接高效能append方式的寫入到本地磁碟檔案裡。
當然,這裡後臺執行緒會有一整套完善的機制,比如說一個磁碟檔案有固定大小,如果達到了一定大小,自動開啟一個新的磁碟檔案來寫入資料。
二、埋下隱患
好!通過上面一套機制,即使是高峰期,也能順利的抗住高併發的請求,一切看起來都很美好!
但是,當時這個降級機制在開發時,我們採取的思路,為後面埋下了隱患!
當時採取的思路是:如果current緩衝區寫滿了之後,所有的執行緒全部陷入一個while迴圈無限等待。
等到什麼時候呢?一直需要等到ready緩衝區的資料被刷到磁碟檔案之後,清空掉ready緩衝區,然後跟current緩衝區進行交換。
這樣current緩衝區要再次變為空的緩衝區,才可以讓工作執行緒繼續寫入資料。
但是大家有沒有考慮過一個異常的情況有可能會發生?
就是後臺執行緒重新整理ready緩衝區的資料到磁碟檔案,實際上也是需要一點時間的。
萬一在他重新整理資料到磁碟檔案的過程中,current緩衝區突然也被寫滿了呢?
此時就會導致系統的所有工作執行緒無法寫入current緩衝區,執行緒全部卡死。
給大家上一張圖,看看這個問題!
這個就是系統的降級機制的雙緩衝機制最根本的問題了,在開發好這套降級機制之後,採用正常的請求壓力測試過,發現兩塊緩衝區在設定為512kb的情況下,運作良好,沒有什麼問題。
三、高峰請求,問題爆發
但是問題就出在高峰期上了。某一次高峰期,系統請求壓力達到了平時的10倍以上。
當然正常流程下,高峰期的時候,寫請求其實也是直接全部寫到MQ中介軟體叢集去的,所以哪怕你高峰期流量增加10倍也無所謂,MQ叢集是可以天然抗高併發的。
但是當時不幸的是,在高峰期的時候,MQ中介軟體叢集突然臨時故障,這也是一年遇不到幾次的。
這就導致這個系統突然觸發了降級機制,然後就開始寫入資料到記憶體雙緩衝裡面去。
要知道,此時是高峰期啊,請求量是平時正常的10倍!因此10倍的請求壓力瞬間導致了一個問題的發生。
這個問題就是瞬時湧入的高併發請求一下將current緩衝區寫滿,然後兩個緩衝區交換,後臺執行緒開始重新整理ready緩衝區的資料到磁碟檔案裡去。
結果因為高峰期請求湧入過快,導致ready緩衝區的資料還沒來得及重新整理到磁碟檔案,此時current緩衝區又突然寫滿了。。。
這就尷尬了,線上系統瞬間開始出現異常。。。
典型的表現就是,所有機器上部署的例項全部執行緒都卡死,處於wait的狀態。
四、定位問題,對症下藥
於是,這套系統開始在高峰期無法響應任何請求。後來經過線上故障緊急排查、定位和搶修,才解決了這個問題。
其實說來解決方法也很簡單,我們通過jvm dump出來快照進行分析,檢視系統的執行緒具體是卡在哪個環節,然後發現大量執行緒卡死在等待current緩衝區的地方。
這就很明顯知道原因了,解決方法就是對線上系統擴容雙段緩衝的大小,從512kb擴容到一個緩衝區10mb。
這樣線上上高峰期的情況下,也可以穩穩的讓降級機制的雙緩衝機制流暢的執行,不會說瞬間高峰湧入的請求打滿兩塊緩衝區。
因為緩衝區越大,就可以讓ready緩衝區被flush到磁碟檔案的過程中,current緩衝區沒那麼快被打滿。
但是這個線上故障反饋出來的一個教訓,就是對系統設計和開發的任何較為複雜的機制,都必須要參照線上高峰期的最大流量來壓力測試。只有這樣,才能確保任何在系統上線的複雜機制可以經得起線上高峰期的流量的考驗。
End
如有收穫,請幫忙轉發,您的鼓勵是作者最大的動力,謝謝!
一大波微服務、分散式、高併發、高可用的原創系列文章正在路上
歡迎掃描下方二維碼,持續關注:
石杉的架構筆記(id:shishan100)
十餘年BAT架構經驗傾囊相授
推薦閱讀:2、【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?
3、【效能優化之道】每秒上萬併發下的Spring Cloud引數優化實戰
6、大規模叢集下Hadoop NameNode如何承載每秒上千次的高併發訪問
7、【效能優化的祕密】Hadoop如何將TB級大檔案的上傳效能優化上百倍
8、拜託,面試請不要再問我TCC分散式事務的實現原理坑爹呀!
9、【坑爹呀!】最終一致性分散式事務如何保障實際生產中99.99%高可用?
11、【眼前一亮!】看Hadoop底層演算法如何優雅的將大規模叢集效能提升10倍以上?
16、億級流量系統架構之如何設計全鏈路99.99%高可用架構
18、大白話聊聊Java併發面試問題之volatile到底是什麼?
19、大白話聊聊Java併發面試問題之Java 8如何優化CAS效能?
20、大白話聊聊Java併發面試問題之談談你對AQS的理解?
21、大白話聊聊Java併發面試問題之公平鎖與非公平鎖是啥?
22、大白話聊聊Java併發面試問題之微服務註冊中心的讀寫鎖優化
23、網際網路公司的面試官是如何360°無死角考察候選人的?(上篇)
24、網際網路公司面試官是如何360°無死角考察候選人的?(下篇)
25、Java進階面試系列之一:哥們,你們的系統架構中為什麼要引入訊息中介軟體?
26、【Java進階面試系列之二】:哥們,那你說說系統架構引入訊息中介軟體有什麼缺點?
27、【行走的Offer收割機】記一位朋友斬獲BAT技術專家Offer的面試經歷
28、【Java進階面試系列之三】哥們,訊息中介軟體在你們專案裡是如何落地的?