高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

石杉的架構筆記發表於2019-01-15

歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100)

週一至週五早8點半!精品技術文章準時送上!

目錄

(1)前情提示

(2)保證投遞訊息不丟失的confirm機制

(3)confirm機制的程式碼實現

(4)confirm機制投遞訊息的高延遲性

(5)高併發下如何投遞訊息才能不丟失

(6)訊息中介軟體全鏈路100%資料不丟失能做到嗎?

1、前情提示

上篇文章:《面試大殺器:訊息中介軟體如何實現消費吞吐量的百倍優化?》,我們分析了RabbitMQ開啟手動ack機制保證消費端資料不丟失的時候,prefetch機制對消費者的吞吐量以及記憶體消耗的影響。

通過分析,我們知道了prefetch過大容易導致記憶體溢位,prefetch過小又會導致消費吞吐量過低,所以在實際專案中需要慎重測試和設定。

這篇文章,我們轉移到訊息中介軟體的生產端,一起來看看如何保證投遞到MQ的資料不丟失。

如果投遞出去的訊息在網路傳輸過程中丟失,或者在RabbitMQ的記憶體中還沒寫入磁碟的時候當機,都會導致生產端投遞到MQ的資料丟失。

而且丟失之後,生產端自己還感知不到,同時還沒辦法來補救。

下面的圖就展示了這個問題。

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

所以本文呢,我們就來逐步分析一下。

2、保證投遞訊息不丟失的confirm機制

其實要解決這個問題,相信大家看過之前的消費端ack機制之後,也都猜到了。

很簡單,就是生產端(比如上圖的訂單服務)首先需要開啟一個confirm模式,接著投遞到MQ的訊息,如果MQ一旦將訊息持久化到磁碟之後,必須也要回傳一個confirm訊息給生產端。

這樣的話,如果生產端的服務接收到了這個confirm訊息,就知道是已經持久化到磁碟了。

否則如果沒有接收到confirm訊息,那麼就說明這條訊息半路可能丟失了,此時你就可以重新投遞訊息到MQ去,確保訊息不要丟失。

而且一旦你開啟了confirm模式之後,每次訊息投遞也同樣是有一個delivery tag的,也是起到唯一標識一次訊息投遞的作用。

這樣,MQ回傳ack給生產端的時候,會帶上這個delivery tag。你就知道具體對應著哪一次訊息投遞了,可以刪除這條訊息。

此外,如果RabbitMQ接收到一條訊息之後,結果內部出錯發現無法處理這條訊息,那麼他會回傳一個nack訊息給生產端。此時你就會感知到這條訊息可能處理有問題,你可以選擇重新再次投遞這條訊息到MQ去。

或者另一種情況,如果某條訊息很長時間都沒給你回傳ack/nack,那可能是極端意外情況發生了,資料也丟了,你也可以自己重新投遞訊息到MQ去。

通過這套confirm機制,就可以實現生產端投遞訊息不會丟失的效果。大家來看看下面的圖,一起來感受一下。

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

3、confirm機制的程式碼實現

下面,我們再來看看confirm機制的程式碼實現:

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

4、confirm機制投遞訊息的高延遲性

這裡有一個很關鍵的點,就是一旦啟用了confirm機制投遞訊息到MQ之後,MQ是不保證什麼時候會給你一個ack或者nack的。

因為RabbitMQ自己內部將訊息持久化到磁碟,本身就是通過非同步批量的方式來進行的。

正常情況下,你投遞到RabbitMQ的訊息都會先駐留在記憶體裡,然後過了幾百毫秒的延遲時間之後,再一次性批量把多條訊息持久化到磁碟裡去。

這樣做,是為了兼顧高併發寫入的吞吐量和效能的,因為要是你來一條訊息就寫一次磁碟,那麼效能會很差,每次寫磁碟都是一次fsync強制刷入磁碟的操作,是很耗時的。

所以正是因為這個原因,你開啟了confirm模式之後,很可能你投遞出去一條訊息,要間隔幾百毫秒之後,MQ才會把訊息寫入磁碟,接著你才會收到MQ回傳過來的ack訊息,這個就是所謂confirm機制投遞訊息的高延遲性。

大家看看下面的圖,一起來感受一下。

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

5、高併發下如何投遞訊息才能不丟失

大家可以考慮一下,在生產端高併發寫入MQ的場景下,你會面臨兩個問題:

1、你每次寫一條訊息到MQ,為了等待這條訊息的ack,必須把訊息儲存到一個儲存裡。

並且這個儲存不建議是記憶體,因為高併發下訊息是很多的,每秒可能都幾千甚至上萬的訊息投遞出去,訊息的ack要等幾百毫秒的話,放記憶體可能有記憶體溢位的風險。

2、絕對不能以同步寫訊息 + 等待ack的方式來投遞,那樣會導致每次投遞一個訊息都同步阻塞等待幾百毫秒,會導致投遞效能和吞吐量大幅度下降。

針對這兩個問題,相對應的方案其實也呼之欲出了。

首先,用來臨時存放未ack訊息的儲存需要承載高併發寫入,而且我們不需要什麼複雜的運算操作,這種儲存首選絕對不是MySQL之類的資料庫,而建議採用kv儲存。kv儲存承載高併發能力極強,而且kv操作效能很高。

其次,投遞訊息之後等待ack的過程必須是非同步的,也就是類似上面那樣的程式碼,已經給出了一個初步的非同步回撥的方式。

訊息投遞出去之後,這個投遞的執行緒其實就可以返回了,至於每個訊息的非同步回撥,是通過在channel註冊一個confirm監聽器實現的。

收到一個訊息ack之後,就從kv儲存中刪除這條臨時訊息;收到一個訊息nack之後,就從kv儲存提取這條訊息然後重新投遞一次即可;也可以自己對kv儲存裡的訊息做監控,如果超過一定時長沒收到ack,就主動重發訊息。

大家看看下面的圖,一起來體會一下:

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

6、訊息中介軟體全鏈路100%資料不丟失能做到嗎?

到此為止,我們已經把生產端和消費端如何保證訊息不丟失的相關技術方案結合RabbitMQ這種中介軟體都給大家分析過了。

其實,架構思想是通用的, 無論你用的是哪一種MQ中介軟體,他們提供的功能是不太一樣的,但是你都需要考慮如下幾點:

生產端如何保證投遞出去的訊息不丟失:訊息在半路丟失,或者在MQ記憶體中當機導致丟失,此時你如何基於MQ的功能保證訊息不要丟失?

MQ自身如何保證訊息不丟失:起碼需要讓MQ對訊息是有持久化到磁碟這個機制。

消費端如何保證消費到的訊息不丟失:如果你處理到一半消費端當機,導致訊息丟失,此時怎麼辦?

目前來說,我們初步的藉著RabbitMQ舉例,已經把從前到後一整套技術方案的原理、設計和實現都給大家分析了一遍了。

但是此時真的能做到100%資料不丟失嗎?恐怕未必,大家再考慮一下個特殊的場景。

生產端投遞了訊息到MQ,而且持久化到磁碟並且回傳ack給生產端了。

但是此時MQ還沒投遞訊息給消費端,結果MQ部署的機器突然當機,而且因為未知的原因磁碟損壞了,直接在物理層面導致MQ持久化到磁碟的資料找不回來了。

這個大家千萬別以為是開玩笑的,大家如果留意留意行業新聞,這種磁碟損壞導致資料丟失的是真的有的。

那麼此時即使你把MQ重啟了,磁碟上的資料也丟失了,資料是不是還是丟失了?

你說,我可以用MQ的叢集機制啊,給一個資料做多個副本,比如後面我們就會給大家分析RabbitMQ的映象叢集機制,確實可以做到資料多副本。

但是即使資料多副本,一定可以做到100%資料不丟失?

比如說你的機房突然遇到地震,結果機房裡的機器全部沒了,資料是不是還是全丟了?

說這個,並不是說要抬槓。而是告訴大家,技術這個東西,100%都是理論上的期望。

應該說,我們凡事都朝著100%去做,但是理論上是不可能完全做到100%保證的,可能就是做到99.9999%的可能性資料不丟失,但是還是有千萬分之一的概率會丟失。

當然,從實際的情況來說,能做到這種地步,其實基本上已經基本資料不會丟失了。

end

如有收穫,請幫忙轉發,您的鼓勵是作者最大的動力,謝謝!

一大波微服務、分散式、高併發、高可用的原創系列文章正在路上

歡迎掃描下方二維碼,持續關注:

高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?【石杉的架構筆記】

石杉的架構筆記(id:shishan100)

十餘年BAT架構經驗傾囊相授

推薦閱讀:

1、拜託!面試請不要再問我Spring Cloud底層原理

2、【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?

3、【效能優化之道】每秒上萬併發下的Spring Cloud引數優化實戰

4、微服務架構如何保障雙11狂歡下的99.99%高可用

5、兄弟,用大白話告訴你小白都能聽懂的Hadoop架構原理

6、大規模叢集下Hadoop NameNode如何承載每秒上千次的高併發訪問

7、【效能優化的祕密】Hadoop如何將TB級大檔案的上傳效能優化上百倍

8、拜託,面試請不要再問我TCC分散式事務的實現原理!

9、【坑爹呀!】最終一致性分散式事務如何保障實際生產中99.99%高可用?

10、拜託,面試請不要再問我Redis分散式鎖的實現原理!

11、【眼前一亮!】看Hadoop底層演算法如何優雅的將大規模叢集效能提升10倍以上?

12、億級流量系統架構之如何支撐百億級資料的儲存與計算

13、億級流量系統架構之如何設計高容錯分散式計算系統

14、億級流量系統架構之如何設計承載百億流量的高效能架構

15、億級流量系統架構之如何設計每秒十萬查詢的高併發架構

16、億級流量系統架構之如何設計全鏈路99.99%高可用架構

17、七張圖徹底講清楚ZooKeeper分散式鎖的實現原理

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進階面試系列之三】哥們,訊息中介軟體在你們專案裡是如何落地的?

29、【Java進階面試系列之四】扎心!線上服務當機時,如何保證資料100%不丟失?

30、一次JVM FullGC的背後,竟隱藏著驚心動魄的線上生產事故!

31、【高併發優化實踐】10倍請求壓力來襲,你的系統會被擊垮嗎?

32、【Java進階面試系列之五】訊息中介軟體叢集崩潰,如何保證百萬生產資料不丟失?

33、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(上)?

34、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(中)?

35、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(下)?

36、億級流量架構第二彈:你的系統真的無懈可擊嗎?

37、億級流量系統架構之如何保證百億流量下的資料一致性(上)

38、億級流量系統架構之如何保證百億流量下的資料一致性(中)?

39、億級流量系統架構之如何保證百億流量下的資料一致性(下)?

40、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(1)

41、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(2

42、面試大殺器:訊息中介軟體如何實現消費吞吐量的百倍優化?

作者:石杉的架構筆記 連結:juejin.im/post/5c263a… 來源:掘金 著作權歸作者所有,轉載請聯絡作者獲得授權!

相關文章