前言
上篇文章,王子通過一個小案例和小伙伴們一起分析了一下消息是如何丟失的,但沒有提出具體的解決方案。
我們已經知道發生消息丟失的原因大體上分為三個部分:
1.生產者發送消息到MQ這一過程導致消息丟失
2.MQ自己發生故障導致消息丟失
3.消費者拿到消息后,由於操作不當導致消息丟失
接下來我們就針對第一種情況,聊一聊如何解決生產者發送消息過程中的消息丟失問題。
先發送half消息到MQ
針對於這一問題,RocketMQ是自帶一套解決方案的,就是事務消息。今天我們就來看一下事務消息的實現流程。
案例還是上次的案例,當用戶通過訂單系統下訂單支付的時候,在訂單支付成功后,會發送消息給MQ,但是這樣的流程是無法保證事務性的。
當我們引入事務消息后,其實訂單系統是不會先去執行CRUD的操作的,而是先發送一條half消息給MQ,這個half消息其實就是訂單完成支付的消息,你可以理解為它的狀態是half狀態。
而積分系統是無法消費half狀態的消息的。
訂單系統發送了half消息后就會等待MQ給出成功的響應,如下圖:
看到這里有些小伙伴可能會問,為什么要發送half消息呢?
其實大家可以想一下,假如我們不發送half消息,直接去操作數據庫,把訂單支付業務走完,然后再去發送消息給MQ,結果發送過程中發生了異常,這就導致了積分系統無法消費到消息,就會導致支付成功,而積分沒有發放的情況。
所以我們先發一條half消息,就是為了先確認一下能否正常發送消息,或者說確認MQ是不是還活着,並且告訴MQ接下來的消息很重要,不能丟失掉。
half消息寫入失敗怎么辦
half消息的發送也是可能失敗的,可能因為報錯、MQ自己掛了、或者網絡原因導致消息發送失敗。
那訂單系統就會得到這一反饋,接着就應該進行回滾操作,比如訂單關閉,退款等操作。
half消息寫入成功,並得到響應
那么假如half消息發送成功,並得到了成功的響應后,訂單系統應該怎么做呢?
這個時候,訂單系統就應該去操作數據庫,完成自己的業務功能了。
因為half消息發送成功,表示MQ可以正常接收消息。
half消息寫入成功,沒有得到響應
那么假如half消息發送成功,但是沒有得到MQ的成功響應,會怎么辦呢?
這個時候,half消息已經正常的存儲到了MQ中,但訂單系統遲遲不能得到響應,可能會報一些網絡超時的錯誤,訂單系統就去執行回滾操作了。
那么對於這條half消息該怎么處理呢?
這就要說到RocketMQ的補償機制了,它會去掃描half消息,如果這條half消息遲遲沒有被rollback或者commit,一定時間后就會回調訂單系統的一個補償接口,判斷一下這步操作是成功了還是失敗了。
如果成功了,那就重新發送commit消息給MQ,失敗了,重新發送rollback消息給MQ。后文會介紹rollback和commit消息。
數據庫操作發生異常
那么接下來如果訂單系統在執行數據庫的時候發生了異常怎么辦呢?
這個時候數據庫本身是有事務機制的,同時我們再發送一條rollback消息給MQ就可以了。
這個時候MQ接收到rollback消息后,就會把之前的half消息給作廢掉了。
訂單業務完成后
那么訂單系統自己的業務成功完成后接着做什么呢?
這個時候就要發送一條commit消息給MQ了,讓MQ把之前的half消息執行commit操作,之后積分系統就可以看到這條消息了。
rollback或者commit消息發送失敗怎么辦
rollback或者commit消息也是可能發送失敗的,這個時候其實也很簡單。
上文中我們已經說到了RocketMQ的補償機制,所以無論訂單系統本身是要發送rollback消息還是commit消息,如果發送失敗,MQ的補償機制就會掃描這條half消息,一定時間之后回調訂單系統的補償接口,判斷執行是否成功了,然后重新發送消息給MQ就可以了
總結
今天我們通過對RocketMQ發送消息這一過程進行各種情況的分析,會發現,開啟事務消息流程后,生產者發送消息到MQ這一過程的消息可靠性是可以得到保證的。
如果有小伙伴覺得有些情況還是沒有考慮到,歡迎評論區留言一起討論。
下篇文章我們將深入探索一下事務消息的底層實現原理,歡迎小伙伴們圍觀。
往期文章推薦: