前言
通過之前文章的閱讀,有關RocketMQ的底層原理相信小伙伴們已經有了一個比較清晰的認識。
那么接下來王子想跟大家討論一個話題,如果我們的項目中引入了MQ,勢必要面對的一個問題,就是消息丟失問題,今天我們就來聊聊消息是怎么丟失的。
現在假設我們的業務是這樣的,用戶通過訂單系統下了一個訂單,訂單系統完成支付后會發送消息給RocketMQ,然后積分系統會從RocketMQ中消費消息,去給用戶增加積分,如下圖:
但是突然有一天有用戶反映,支付訂單之后,自己的積分並沒有增長,這是為什么呢?
經過排查日志,我們只發現了推送消息給MQ的日志,而沒有發現積分系統消費這條消息的日志,這就導致了積分系統並沒有給用戶發放積分。
也就是說,消息在傳輸過程中丟失了。
在系統的核心鏈路中,如果發生消息丟失的問題,可能會產生惡劣的后果,為了解決此類問題,我們必須弄明白什么時候會發生消息丟失。
訂單系統推送消息過程中會丟失消息嗎?
我們先來看一下整個流程的第一步,訂單系統在支付成功之后,一定會把支付成功的消息推送給MQ,那么在這個推送的過程中,消息可能丟失嗎?
答案是肯定的,一定會存在消息丟失的情況。
比較常見的情況就是網絡抖動,在推送消息這一過程中是通過網絡進行通信的,那么這個時候如果恰巧網絡出現了故障,導致通信失敗了,那么這個消息必然就不會成功的推送到MQ中。
那除了網絡抖動外,還有沒有其他的情況導致推送失敗呢?
其實情況有很多,比如MQ成功接收到了消息,但是MQ本身的網絡模塊的代碼出現了異常,可能是內部實現的bug,導致消息沒有成功處理。
或者當我們推送消息給一個MQ的主從集群的時候,剛好遇到Leader節點出現故障,其他的Follower正在嘗試切換為Leader,這個過程中也可能導致消息丟失。
類似的問題還有其他的。
所以我們首先要明確一點,無論我們使用任何MQ中間件的時候,你發送出的消息都不一定能成功,而失敗的時候有可能會在你的代碼里發生異常,也有可能不會拋出異常,具體要看什么情況導致的發送失敗。
MQ接收到消息后,自己會把消息弄丟嗎?
接下來假設我們訂單系統推送到MQ這一過程沒有任何問題,消息成功到達了MQ中,此時訂單系統會認為消息寫入成功了,那么這時候消息就一定不會丟失了嗎?
答案是否定的,這個時候也不能保證消息的不丟失,我們來分析一下。
通過之前文章的了解,相信大家都還記得,當消息寫入到MQ后,MQ會把消息先寫入到os cache,也就是操作系統的緩存區中,本質也是內存,如下圖:
也就是說,你認為發送成功的消息,可能只存在於內存中,還沒到磁盤中。
那么如果這個時候機器宕機了,os cache中的消息數據將會跟着丟失掉,是不是這個理。
那么現在假設消息已經刷新到磁盤上了,是不是就可以保證萬無一失了呢?
顯然這個時候也是不能完全保證的,因為雖然你把數據保存到了磁盤中,但是如果磁盤發生了故障,數據還是會丟失掉。
如果大家平時有了解一下新聞熱點,會聽說過某某互聯網公司,由於數據存儲在磁盤上沒有冗余備份,結果磁盤發生故障導致好多年的核心數據全部丟失,大量工作都功虧一簣,這就是血淋淋的教訓。
積分系統消費到了消息就能保證消息的不丟失了嗎?
那么到現在,經歷了重重困境,假設積分系統終於能夠消費到這條消息了,那么它就能安穩的把積分正常的發放給用戶嗎?
答案依然是否定的。
看過之前文章的小伙伴們應該還記得消費者在進行消費時,是有一個offset的概念的。
這個offset說白了就是個進度標識,讓MQ知道消費者消費到了哪,下次好接着向下消費。
現在假設我們有兩條消息,offset為1和2。
假設我們的積分系統接收到了消息1,那么消息1就在積分系統的內存中,正要准備給用戶發放積分。
而默認情況下,消費者會自動提交已經消費的消息的offset,所以當積分系統獲取消息后,可能直接就把消息1的offset提交給了MQ,標識為已經處理了這條消息。
那么此時,如果積分系統突然宕機,還未發放積分給用戶,那么這條消息1自然就丟失了,因為MQ已經把他標記成了已處理,實際積分系統還未處理。
所以消費者獲得消息后也是可能發生消息丟失的。
總結
好了,看過今天的文章,相信小伙伴們對於RocketMQ的消息是怎么丟失的會有一個更深刻的印象。
總結起來就是以下幾點:
1.生產者發送消息到MQ這一過程導致消息丟失
2.MQ自己發生故障導致消息丟失
3.消費者拿到消息后,由於操作不當導致消息丟失
所以任何的技術引入生產環境都是有風險的,引入前我們一定要做好功課。
今天的文章就說到這,小伙伴們可能會問王子,聊了這么多,到底應該如何解決掉消息丟失的問題呢?
別急,我們下篇文章就會有解決方案了。
那么小伙伴們針對MQ的消息丟失問題是怎么解決的呢,歡迎大家留言和王子一起討論。
往期文章推薦: