溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?

發(fā)布時(shí)間:2020-08-11 08:55:18 來源:ITPUB博客 閱讀:167 作者:石杉的架構(gòu)筆記 欄目:編程語言

本文來自貍貓技術(shù)窩專欄《從零開始帶你成為消息中間件實(shí)戰(zhàn)高手》,是作者原子彈大俠開放的試讀

1、解決消息丟失的第一個(gè)問題:訂單系統(tǒng)推送消息領(lǐng)丟失

既然我們已經(jīng)明確了消息在基于MQ傳輸?shù)倪^程中可能丟失的幾個(gè)地方,那么我們接著就得一步一步考慮如何去解決各個(gè)環(huán)節(jié)丟失消息的問題,首先要解決的第一個(gè)問題,就是訂單系統(tǒng)推送消息到MQ的過程中,可能消息就丟失了。

之前我們也說過了,可能在訂單系統(tǒng)推送消息到MQ的過程中,就因?yàn)槌R姷木W(wǎng)絡(luò)故障之類的問題,導(dǎo)致消息就丟失了,這里我們可以看一下下圖中的示意。           深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失? 而在RocketMQ中,有一個(gè)非常強(qiáng)悍有力的功能,就是事務(wù)消息的功能,憑借這個(gè)事務(wù)級的消息機(jī)制,就可以讓我們確保訂單系統(tǒng)推送給出去的消息一定會(huì)成功寫入MQ里,絕對不會(huì)半路就搞丟了。

今天我們就來系統(tǒng)的分析一下RocketMQ的事務(wù)消息機(jī)制的原理。

2、發(fā)送half消息到MQ去,試探一下MQ是否正常

首先作為我們的訂單系統(tǒng)而言,假設(shè)他收到了一個(gè)訂單支付成功的通知之后,他必然是需要在自己的訂單數(shù)據(jù)庫里做一些增刪改操作的,比如更新訂單狀態(tài)之類的。

可能有的朋友會(huì)覺得,訂單系統(tǒng)不就是先在自己數(shù)據(jù)庫里做一些增刪改操作,然后就直接發(fā)個(gè)消息到MQ去,讓其他關(guān)注這個(gè)訂單支付成功消息的系統(tǒng)去從MQ獲取消息做對應(yīng)的處理就可以了么?

事實(shí)上還真不是這么簡單。

在基于RocketMQ的事務(wù)消息機(jī)制中,我們首先要讓訂單系統(tǒng)去發(fā)送一條half消息到MQ去,這個(gè)half消息本質(zhì)就是一個(gè)訂單支付成功的消息,只不過你可以理解為他這個(gè)消息的狀態(tài)是half狀態(tài),這個(gè)時(shí)候紅包系統(tǒng)是看不見這個(gè)half消息的,然后我們?nèi)サ却邮者@個(gè)half消息寫入成功的響應(yīng)通知

我們看下面的圖

          深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?      

看到這兒可能有的朋友就開始有點(diǎn)郁悶了,可能有的人覺得你沒事兒先發(fā)個(gè)half消息給MQ干什么?

大家先別著急,你可以想一下,假設(shè)你二話不說讓訂單系統(tǒng)直接做了本地的數(shù)據(jù)庫操作,比如訂單狀態(tài)都更新為了已完成,然后你再發(fā)送消息給MQ,結(jié)果報(bào)出一堆異常,發(fā)現(xiàn)MQ掛了。

這個(gè)時(shí)候,必然導(dǎo)致你沒法通過消息通知到紅包系統(tǒng)去派發(fā)紅包,那用戶一定會(huì)發(fā)現(xiàn)自己訂單支付了,結(jié)果紅包沒收到。

所以,在這里我們首先第一件事,不是先讓訂單系統(tǒng)做一些增刪改操作,而是先發(fā)一個(gè)half消息給MQ以及收到他的成功的響應(yīng),初步先跟MQ做個(gè)聯(lián)系和溝通

大概這個(gè)意思就是說,確認(rèn)一下MQ還活著,MQ也知道你后續(xù)可能想發(fā)送一條很關(guān)鍵的不希望丟失的消息給他了!

3、萬一要是half消息寫入失敗了呢?

這里我們先來分析第一種情況,萬一你訂單系統(tǒng)寫half消息給MQ就失敗了呢?

可能你發(fā)現(xiàn)報(bào)錯(cuò)了,可能MQ就掛了,或者這個(gè)時(shí)候網(wǎng)絡(luò)就是故障了,所以導(dǎo)致你的half消息都沒發(fā)送成功,總之你現(xiàn)在肯定沒法跟MQ通信了。

這個(gè)時(shí)候你的訂單系統(tǒng)就應(yīng)該執(zhí)行一系列的回滾操作,比如對訂單狀態(tài)做一個(gè)更新,讓狀態(tài)變成“關(guān)閉交易”,同時(shí)通知支付系統(tǒng)自動(dòng)進(jìn)行退款,這才是正確的做法

因?yàn)槟阌唵坞m然支付了,但是包括派發(fā)紅包、發(fā)送優(yōu)惠券之類的后續(xù)操作是無法執(zhí)行的,所以此時(shí)必然應(yīng)該把錢款退還給用戶,說交易失敗了。

這里給大家插播一個(gè)我曾經(jīng)親身經(jīng)歷過的一個(gè)事情,曾經(jīng)有一次在一家便利店進(jìn)行購物的時(shí)候,我這里都已經(jīng)顯示掃碼支付成功了,但是店員那邊說在等待他們系統(tǒng)確認(rèn)

結(jié)果等了一會(huì)兒,系統(tǒng)顯示后臺系統(tǒng)有異常,交易失敗了,然后過了一會(huì)兒就讓支付寶自動(dòng)退款給我了。

其實(shí)這就是類似的例子。

4、half消息成功之后,訂單系統(tǒng)完成自己的任務(wù)

接著我們來考慮第二種情況,你的half消息寫成功了,這時(shí)你應(yīng)該干什么呢?

這時(shí)你的訂單系統(tǒng)就應(yīng)該在自己本地的數(shù)據(jù)庫里執(zhí)行一些增刪改操作了,因?yàn)橐坏﹉alf消息寫成功了,就說明MQ肯定已經(jīng)收到這條消息了,MQ還活著,而且目前你是可以跟MQ正常溝通的。

我們看下面的圖,示意了下一步是訂單系統(tǒng)執(zhí)行自己的增刪改操作。 深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?

5、如果訂單系統(tǒng)的本地事務(wù)執(zhí)行失敗了怎么辦?

接著我們繼續(xù)看下一種情況,萬一訂單系統(tǒng)更新自己的數(shù)據(jù)庫失敗了怎么辦?

比如訂單系統(tǒng)的數(shù)據(jù)庫當(dāng)時(shí)也有網(wǎng)絡(luò)異常,或者數(shù)據(jù)庫掛了,總而言之,就是你想把訂單更新為“已完成”這個(gè)狀態(tài),是干不成了。

這個(gè)時(shí)候其實(shí)也很簡單,直接就是讓訂單系統(tǒng)發(fā)送一個(gè)rollback請求給MQ就可以了

這個(gè)意思就是說,你可以把之前我發(fā)給你的half消息給刪除掉了,因?yàn)槲易约哼@里都出問題了,已經(jīng)無力跟你繼續(xù)后續(xù)的流程了。

我們看下面的圖,我給出了這個(gè)示意

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?

當(dāng)然你發(fā)送rollback請求給MQ刪除那個(gè)half消息之后,你的訂單系統(tǒng)就必須走后續(xù)的回退流程了,就是通知支付系統(tǒng)退款。

當(dāng)然這里可能還有一些訂單系統(tǒng)自己的高可用降級的機(jī)制需要考慮,比如數(shù)據(jù)庫無法更新了,此時(shí)你可能需要在機(jī)器本地磁盤文件里寫入訂單支付失敗的記錄。

然后你可以開一個(gè)后臺線程在MySQL數(shù)據(jù)庫恢復(fù)之后 ,再把訂單狀態(tài)更新為“已關(guān)閉”。不過這個(gè)不在我們討專欄的范圍之內(nèi)。

6、如果訂單系統(tǒng)完成了本地事務(wù)之后,接著干什么?

如果訂單系統(tǒng)成功完成了本地的事務(wù)操作,比如把訂單狀態(tài)都更新為“已完成”了,此時(shí)你就可以發(fā)送一個(gè)commit請求給MQ,要求讓MQ對之前的half消息進(jìn)行commit操作,讓紅包系統(tǒng)可以看見這個(gè)訂單支付成功消息

我們看下面的圖

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?之前我們也提到過了,所謂的half消息實(shí)際就是訂單支付成功的消息,只不過他的狀態(tài)是half

也就是說,他是half狀態(tài)的時(shí)候,紅包系統(tǒng)是看不見他的,沒法獲取到這條消息。必須等到訂單系統(tǒng)執(zhí)行commit請求,消息被commit之后,紅包系統(tǒng)才可以看到和獲取這條消息進(jìn)行后續(xù)處理。

7、讓流程嚴(yán)謹(jǐn)一些:如果發(fā)送half消息成功了,但是沒收到響應(yīng)呢?

大致的事務(wù)消息的流程是講完了,但是接著讓我們來進(jìn)行比較嚴(yán)謹(jǐn)?shù)姆治?/span>

如果我們是把half消息發(fā)送給MQ了,MQ給保存下來了,但是MQ返回給我們的響應(yīng)我們沒收到呢?此時(shí)會(huì)發(fā)生什么事情?

這個(gè)時(shí)候我們沒收到響應(yīng),可能就會(huì)網(wǎng)絡(luò)超時(shí)報(bào)錯(cuò),也可能直接有其他的異常錯(cuò)誤,這時(shí)訂單系統(tǒng)會(huì)誤以為是發(fā)送half消息到MQ失敗了,訂單系統(tǒng)就直接會(huì)執(zhí)行退款流程了,訂單狀態(tài)也會(huì)標(biāo)記為“已關(guān)閉”。

我們看下面的圖的示意

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?

但是這時(shí)MQ已經(jīng)存儲(chǔ)下來一條half消息了,那對這個(gè)消息怎么處理?

其實(shí)RocketMQ這里有一個(gè)補(bǔ)償流程,他會(huì)去掃描自己處于half狀態(tài)的消息,如果我們一直沒有對這個(gè)消息執(zhí)行commit/rollback操作,超過了一定的時(shí)間,他就會(huì)回調(diào)你的訂單系統(tǒng)的一個(gè)接口,問問你說,這個(gè)消息到底怎么回事?

你到底是打算commit這個(gè)消息還是要rollback這個(gè)消息?

我們看下圖示意

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?這個(gè)時(shí)候我們的訂單系統(tǒng)就得去查一下數(shù)據(jù)庫,看看這個(gè)訂單當(dāng)前的狀態(tài),一下發(fā)現(xiàn)訂單狀態(tài)是“已關(guān)閉”,此時(shí)就知道,你必然是得發(fā)送rollback請求給MQ去刪除之前那個(gè)half消息了!

我們看下圖

深入探究 RocketMQ 事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?

8、如果rollback或者commit發(fā)送失敗了呢?

我們再假設(shè)一種場景,如果訂單系統(tǒng)是收到了half消息寫入成功的響應(yīng)了,同時(shí)嘗試對自己的數(shù)據(jù)庫更新了,然后根據(jù)失敗或者成功去執(zhí)行了rollback或者commit請求,發(fā)送給MQ了

結(jié)果因?yàn)榫W(wǎng)絡(luò)故障,導(dǎo)致rollback或者commit請求發(fā)送失敗了呢?

這個(gè)時(shí)候其實(shí)也很簡單,因?yàn)镸Q里的消息一直是half狀態(tài),所以他過了一定的超時(shí)時(shí)間會(huì)發(fā)現(xiàn)這個(gè)half消息有問題,他會(huì)回調(diào)你的訂單系統(tǒng)的接口

你此時(shí)要判斷一下,這個(gè)訂單的狀態(tài)如果更新為了“已完成”,那你就得再次執(zhí)行commit請求,反之則再次執(zhí)行rollback請求。

本質(zhì)這個(gè)MQ的回調(diào)就是一個(gè)補(bǔ)償機(jī)制,就怕你的half消息響應(yīng)沒收到,或者rollback、commit請求沒發(fā)送成功,所以他會(huì)來找你問問對half消息后續(xù)如何處理。

9、停一下腳本想想上面這個(gè)流程的意義在哪里?

看到這里我們來停下腳步想想,上面這個(gè)流程的意義在哪里呢?

其實(shí)很簡單,如果你的MQ有問題或者網(wǎng)絡(luò)有問題,half消息根本都發(fā)不出去,此時(shí)half消息肯定是失敗的,那么訂單系統(tǒng)就不會(huì)執(zhí)行后續(xù)流程了!

如果要是half消息發(fā)送出去了,但是half消息的響應(yīng)都沒收到,然后執(zhí)行了退款流程,那MQ會(huì)有補(bǔ)償機(jī)制來回調(diào)找你詢問要commit還是rollback,此時(shí)你選擇rollback刪除消息就可以了,不會(huì)執(zhí)行后續(xù)流程!

如果要是訂單系統(tǒng)收到half消息了,結(jié)果訂單系統(tǒng)自己更新數(shù)據(jù)庫失敗了,那么他也會(huì)進(jìn)行回滾,不會(huì)執(zhí)行后續(xù)流程了!

如果要是訂單系統(tǒng)收到half消息了,然后還更新自己數(shù)據(jù)庫成功了,訂單狀態(tài)是“已完成”了,此時(shí)就必然會(huì)發(fā)送commit請求給MQ,一旦消息commit了,那么必然保證紅包系統(tǒng)可以收到這個(gè)消息!

而且即使你commit請求發(fā)送失敗了,MQ也會(huì)有補(bǔ)償機(jī)制,回調(diào)你接口讓你判斷是否重新發(fā)送commit請求

總之,就是你的訂單系統(tǒng)只要成功了,那么必然要保證MQ里的消息是commit了可以讓紅包系統(tǒng)看到他!

所以大家可以結(jié)合我們的圖思考一下上述流程,通過這套事務(wù)消息的機(jī)制,是不是就可以保證我們的訂單系統(tǒng)一旦成功執(zhí)行了數(shù)據(jù)庫操作,就一定會(huì)通知到紅包系統(tǒng)去派發(fā)紅包?至少訂單系統(tǒng)到MQ之間的消息傳輸是不會(huì)有丟失的問題了!


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI