資料內(nèi)容:
2、RocketMQ消息零丟失方案
1》 生產(chǎn)者使用事務(wù)消息機(jī)制保證消息零丟失
這個(gè)結(jié)論比較容易理解,因?yàn)镽ocketMQ的事務(wù)消息機(jī)制就是為了保證零丟失來(lái)設(shè)計(jì)的,并且經(jīng)過(guò)阿里
的驗(yàn)證,肯定是非常靠譜的。
但是如果深入一點(diǎn)的話(huà),我們還是要理解下這個(gè)事務(wù)消息到底是不是靠譜。我們以最常見(jiàn)的電商訂單場(chǎng)
景為例,來(lái)簡(jiǎn)單分析下事務(wù)消息機(jī)制如何保證消息不丟失。我們看下下面這個(gè)流程圖:
1、為什么要發(fā)送個(gè)half消息?有什么用?
這個(gè)half消息是在訂單系統(tǒng)進(jìn)行下單操作前發(fā)送,并且對(duì)下游服務(wù)的消費(fèi)者是不可見(jiàn)的。那這個(gè)消息的
作用更多的體現(xiàn)在確認(rèn)RocketMQ的服務(wù)是否正常。相當(dāng)于嗅探下RocketMQ服務(wù)是否正常,并且通知
RocketMQ,我馬上就要發(fā)一個(gè)很重要的消息了,你做好準(zhǔn)備。
2.half消息如果寫(xiě)入失敗了怎么辦?
如果沒(méi)有half消息這個(gè)流程,那我們通常是會(huì)在訂單系統(tǒng)中先完成下單,再發(fā)送消息給MQ。這時(shí)候?qū)?br />
入消息到MQ如果失敗就會(huì)非常尷尬了。而half消息如果寫(xiě)入失敗,我們就可以認(rèn)為MQ的服務(wù)是有問(wèn)題
的,這時(shí),就不能通知下游服務(wù)了。我們可以在下單時(shí)給訂單一個(gè)狀態(tài)標(biāo)記,然后等待MQ服務(wù)正常后
再進(jìn)行補(bǔ)償操作,等MQ服務(wù)正常后重新下單通知下游服務(wù)。
3.訂單系統(tǒng)寫(xiě)數(shù)據(jù)庫(kù)失敗了怎么辦?
這個(gè)問(wèn)題我們同樣比較下沒(méi)有使用事務(wù)消息機(jī)制時(shí)會(huì)怎么辦?如果沒(méi)有使用事務(wù)消息,我們只能判斷下
單失敗,拋出了異常,那就不往MQ發(fā)消息了,這樣至少保證不會(huì)對(duì)下游服務(wù)進(jìn)行錯(cuò)誤的通知。但是這
樣的話(huà),如果過(guò)一段時(shí)間數(shù)據(jù)庫(kù)恢復(fù)過(guò)來(lái)了,這個(gè)消息就無(wú)法再次發(fā)送了。當(dāng)然,也可以設(shè)計(jì)另外的補(bǔ)
償機(jī)制,例如將訂單數(shù)據(jù)緩存起來(lái),再啟動(dòng)一個(gè)線(xiàn)程定時(shí)嘗試往數(shù)據(jù)庫(kù)寫(xiě)。而如果使用事務(wù)消息機(jī)制,
就可以有一種更優(yōu)雅的方案。
如果下單時(shí),寫(xiě)數(shù)據(jù)庫(kù)失敗(可能是數(shù)據(jù)庫(kù)崩了,需要等一段時(shí)間才能恢復(fù))。那我們可以另外找個(gè)地方
把訂單消息先緩存起來(lái)(Redis、文本或者其他方式),然后給RocketMQ返回一個(gè)UNKNOWN狀態(tài)。這樣
RocketMQ就會(huì)過(guò)一段時(shí)間來(lái)回查事務(wù)狀態(tài)。我們就可以在回查事務(wù)狀態(tài)時(shí)再?lài)L試把訂單數(shù)據(jù)寫(xiě)入數(shù)據(jù)
庫(kù),如果數(shù)據(jù)庫(kù)這時(shí)候已經(jīng)恢復(fù)了,那就能完整正常的下單,再繼續(xù)后面的業(yè)務(wù)。這樣這個(gè)訂單的消息
就不會(huì)因?yàn)閿?shù)據(jù)庫(kù)臨時(shí)崩了而丟失。
4.half消息寫(xiě)入成功后RocketMQ掛了怎么辦?
我們需要注意下,在事務(wù)消息的處理機(jī)制中,未知狀態(tài)的事務(wù)狀態(tài)回查是由RocketMQ的Broker主動(dòng)發(fā)
起的。也就是說(shuō)如果出現(xiàn)了這種情況,那RocketMQ就不會(huì)回調(diào)到事務(wù)消息中回查事務(wù)狀態(tài)的服務(wù)。這
時(shí),我們就可以將訂單一直標(biāo)記為"新下單"的狀態(tài)。而等RocketMQ恢復(fù)后,只要存儲(chǔ)的消息沒(méi)有丟
失,RocketMQ就會(huì)再次繼續(xù)狀態(tài)回查的流程。
5.下單成功后如何優(yōu)雅的等待支付成功?
在訂單場(chǎng)景下,通常會(huì)要求下單完成后,客戶(hù)在一定時(shí)間內(nèi),例如10分鐘,內(nèi)完成訂單支付,支付完成
后才會(huì)通知下游服務(wù)進(jìn)行進(jìn)一步的營(yíng)銷(xiāo)補(bǔ)償。
如果不用事務(wù)消息,那通常會(huì)怎么辦?
最簡(jiǎn)單的方式是啟動(dòng)一個(gè)定時(shí)任務(wù),每隔一段時(shí)間掃描訂單表,比對(duì)未支付的訂單的下單時(shí)間,將超過(guò)
時(shí)間的訂單回收。這種方式顯然是有很大問(wèn)題的,需要定時(shí)掃描很龐大的一個(gè)訂單信息,這對(duì)系統(tǒng)是個(gè)
不小的壓力。
那更進(jìn)一步的方案是什么呢?是不是就可以使用RocketMQ提供的延遲消息機(jī)制。往MQ發(fā)一個(gè)延遲1分
鐘的消息,消費(fèi)到這個(gè)消息后去檢查訂單的支付狀態(tài),如果訂單已經(jīng)支付,就往下游發(fā)送下單的通知。
而如果沒(méi)有支付,就再發(fā)一個(gè)延遲1分鐘的消息。最終在第十個(gè)消息時(shí)把訂單回收。這個(gè)方案就不用對(duì)
全部的訂單表進(jìn)行掃描,而只需要每次處理一個(gè)單獨(dú)的訂單消息。
那如果使用上了事務(wù)消息呢?我們就可以用事務(wù)消息的狀態(tài)回查機(jī)制來(lái)替代定時(shí)的任務(wù)。在下單時(shí),給
Broker返回一個(gè)UNKNOWN的未知狀態(tài)。而在狀態(tài)回查的方法中去查詢(xún)訂單的支付狀態(tài)。這樣整個(gè)業(yè)
務(wù)邏輯就會(huì)簡(jiǎn)單很多。我們只需要配置RocketMQ中的事務(wù)消息回查次數(shù)(默認(rèn)15次)和事務(wù)回查間隔時(shí)
間(messageDelayLevel),就可以更優(yōu)雅的完成這個(gè)支付狀態(tài)檢查的需求。
6、事務(wù)消息機(jī)制的作用
整體來(lái)說(shuō),在訂單這個(gè)場(chǎng)景下,消息不丟失的問(wèn)題實(shí)際上就還是轉(zhuǎn)化成了下單這個(gè)業(yè)務(wù)與下游服務(wù)的業(yè)
務(wù)的分布式事務(wù)一致性問(wèn)題。而事務(wù)一致性問(wèn)題一直以來(lái)都是一個(gè)非常復(fù)雜的問(wèn)題。而RocketMQ的事
務(wù)消息機(jī)制,實(shí)際上只保證了整個(gè)事務(wù)消息的一半,他保證的是訂單系統(tǒng)下單和發(fā)消息這兩個(gè)事件的事
務(wù)一致性,而對(duì)下游服務(wù)的事務(wù)并沒(méi)有保證。但是即便如此,也是分布式事務(wù)的一個(gè)很好的降級(jí)方案。
目前來(lái)看,也是業(yè)內(nèi)最好的降級(jí)方案