本文來自貍貓技術(shù)窩專欄《從零開始帶你成為消息中間件實(shí)戰(zhàn)高手》,是作者原子彈大俠開放的試讀
創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括廣昌網(wǎng)站建設(shè)、廣昌網(wǎng)站制作、廣昌網(wǎng)頁(yè)制作以及廣昌網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,廣昌網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到廣昌省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
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中,有一個(gè)非常強(qiáng)悍有力的功能,就是事務(wù)消息的功能,憑借這個(gè)事務(wù)級(jí)的消息機(jī)制,就可以讓我們確保訂單系統(tǒng)推送給出去的消息一定會(huì)成功寫入MQ里,絕對(duì)不會(huì)半路就搞丟了。
今天我們就來系統(tǒng)的分析一下RocketMQ的事務(wù)消息機(jī)制的原理。
2、發(fā)送half消息到MQ去,試探一下MQ是否正常
首先作為我們的訂單系統(tǒng)而言,假設(shè)他收到了一個(gè)訂單支付成功的通知之后,他必然是需要在自己的訂單數(shù)據(jù)庫(kù)里做一些增刪改操作的,比如更新訂單狀態(tài)之類的。
可能有的朋友會(huì)覺得,訂單系統(tǒng)不就是先在自己數(shù)據(jù)庫(kù)里做一些增刪改操作,然后就直接發(fā)個(gè)消息到MQ去,讓其他關(guān)注這個(gè)訂單支付成功消息的系統(tǒng)去從MQ獲取消息做對(duì)應(yīng)的處理就可以了么?
事實(shí)上還真不是這么簡(jiǎn)單。
在基于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)通知
我們看下面的圖
看到這兒可能有的朋友就開始有點(diǎn)郁悶了,可能有的人覺得你沒事兒先發(fā)個(gè)half消息給MQ干什么?
大家先別著急,你可以想一下,假設(shè)你二話不說讓訂單系統(tǒng)直接做了本地的數(shù)據(jù)庫(kù)操作,比如訂單狀態(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、萬(wàn)一要是half消息寫入失敗了呢?
這里我們先來分析第一種情況,萬(wàn)一你訂單系統(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í)行一系列的回滾操作,比如對(duì)訂單狀態(tài)做一個(gè)更新,讓狀態(tài)變成“關(guān)閉交易”,同時(shí)通知支付系統(tǒng)自動(dòng)進(jìn)行退款,這才是正確的做法
因?yàn)槟阌唵坞m然支付了,但是包括派發(fā)紅包、發(fā)送優(yōu)惠券之類的后續(xù)操作是無(wú)法執(zhí)行的,所以此時(shí)必然應(yīng)該把錢款退還給用戶,說交易失敗了。
這里給大家插播一個(gè)我曾經(jīng)親身經(jīng)歷過的一個(gè)事情,曾經(jīng)有一次在一家便利店進(jìn)行購(gòu)物的時(shí)候,我這里都已經(jīng)顯示掃碼支付成功了,但是店員那邊說在等待他們系統(tǒng)確認(rèn)
結(jié)果等了一會(huì)兒,系統(tǒng)顯示后臺(tái)系統(tǒng)有異常,交易失敗了,然后過了一會(huì)兒就讓支付寶自動(dòng)退款給我了。
其實(shí)這就是類似的例子。
4、half消息成功之后,訂單系統(tǒng)完成自己的任務(wù)
接著我們來考慮第二種情況,你的half消息寫成功了,這時(shí)你應(yīng)該干什么呢?
這時(shí)你的訂單系統(tǒng)就應(yīng)該在自己本地的數(shù)據(jù)庫(kù)里執(zhí)行一些增刪改操作了,因?yàn)橐坏﹉alf消息寫成功了,就說明MQ肯定已經(jīng)收到這條消息了,MQ還活著,而且目前你是可以跟MQ正常溝通的。
我們看下面的圖,示意了下一步是訂單系統(tǒng)執(zhí)行自己的增刪改操作。 
5、如果訂單系統(tǒng)的本地事務(wù)執(zhí)行失敗了怎么辦?
接著我們繼續(xù)看下一種情況,萬(wàn)一訂單系統(tǒng)更新自己的數(shù)據(jù)庫(kù)失敗了怎么辦?
比如訂單系統(tǒng)的數(shù)據(jù)庫(kù)當(dāng)時(shí)也有網(wǎng)絡(luò)異常,或者數(shù)據(jù)庫(kù)掛了,總而言之,就是你想把訂單更新為“已完成”這個(gè)狀態(tài),是干不成了。
這個(gè)時(shí)候其實(shí)也很簡(jiǎn)單,直接就是讓訂單系統(tǒng)發(fā)送一個(gè)rollback請(qǐng)求給MQ就可以了
這個(gè)意思就是說,你可以把之前我發(fā)給你的half消息給刪除掉了,因?yàn)槲易约哼@里都出問題了,已經(jīng)無(wú)力跟你繼續(xù)后續(xù)的流程了。
我們看下面的圖,我給出了這個(gè)示意

當(dāng)然你發(fā)送rollback請(qǐng)求給MQ刪除那個(gè)half消息之后,你的訂單系統(tǒng)就必須走后續(xù)的回退流程了,就是通知支付系統(tǒng)退款。
當(dāng)然這里可能還有一些訂單系統(tǒng)自己的高可用降級(jí)的機(jī)制需要考慮,比如數(shù)據(jù)庫(kù)無(wú)法更新了,此時(shí)你可能需要在機(jī)器本地磁盤文件里寫入訂單支付失敗的記錄。
然后你可以開一個(gè)后臺(tái)線程在MySQL數(shù)據(jù)庫(kù)恢復(fù)之后 ,再把訂單狀態(tài)更新為“已關(guān)閉”。不過這個(gè)不在我們討專欄的范圍之內(nèi)。
6、如果訂單系統(tǒng)完成了本地事務(wù)之后,接著干什么?
如果訂單系統(tǒng)成功完成了本地的事務(wù)操作,比如把訂單狀態(tài)都更新為“已完成”了,此時(shí)你就可以發(fā)送一個(gè)commit請(qǐng)求給MQ,要求讓MQ對(duì)之前的half消息進(jìn)行commit操作,讓紅包系統(tǒng)可以看見這個(gè)訂單支付成功消息
我們看下面的圖
之前我們也提到過了,所謂的half消息實(shí)際就是訂單支付成功的消息,只不過他的狀態(tài)是half
也就是說,他是half狀態(tài)的時(shí)候,紅包系統(tǒng)是看不見他的,沒法獲取到這條消息。必須等到訂單系統(tǒng)執(zhí)行commit請(qǐng)求,消息被commit之后,紅包系統(tǒng)才可以看到和獲取這條消息進(jìn)行后續(xù)處理。
7、讓流程嚴(yán)謹(jǐn)一些:如果發(fā)送half消息成功了,但是沒收到響應(yīng)呢?
大致的事務(wù)消息的流程是講完了,但是接著讓我們來進(jìn)行比較嚴(yán)謹(jǐn)?shù)姆治?/p>
如果我們是把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)閉”。
我們看下面的圖的示意

但是這時(shí)MQ已經(jīng)存儲(chǔ)下來一條half消息了,那對(duì)這個(gè)消息怎么處理?
其實(shí)RocketMQ這里有一個(gè)補(bǔ)償流程,他會(huì)去掃描自己處于half狀態(tài)的消息,如果我們一直沒有對(duì)這個(gè)消息執(zhí)行commit/rollback操作,超過了一定的時(shí)間,他就會(huì)回調(diào)你的訂單系統(tǒng)的一個(gè)接口,問問你說,這個(gè)消息到底怎么回事?
你到底是打算commit這個(gè)消息還是要rollback這個(gè)消息?
我們看下圖示意
這個(gè)時(shí)候我們的訂單系統(tǒng)就得去查一下數(shù)據(jù)庫(kù),看看這個(gè)訂單當(dāng)前的狀態(tài),一下發(fā)現(xiàn)訂單狀態(tài)是“已關(guān)閉”,此時(shí)就知道,你必然是得發(fā)送rollback請(qǐng)求給MQ去刪除之前那個(gè)half消息了!
我們看下圖

8、如果rollback或者commit發(fā)送失敗了呢?
我們?cè)偌僭O(shè)一種場(chǎng)景,如果訂單系統(tǒng)是收到了half消息寫入成功的響應(yīng)了,同時(shí)嘗試對(duì)自己的數(shù)據(jù)庫(kù)更新了,然后根據(jù)失敗或者成功去執(zhí)行了rollback或者commit請(qǐng)求,發(fā)送給MQ了
結(jié)果因?yàn)榫W(wǎng)絡(luò)故障,導(dǎo)致rollback或者commit請(qǐng)求發(fā)送失敗了呢?
這個(gè)時(shí)候其實(shí)也很簡(jiǎn)單,因?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請(qǐng)求,反之則再次執(zhí)行rollback請(qǐng)求。
本質(zhì)這個(gè)MQ的回調(diào)就是一個(gè)補(bǔ)償機(jī)制,就怕你的half消息響應(yīng)沒收到,或者rollback、commit請(qǐng)求沒發(fā)送成功,所以他會(huì)來找你問問對(duì)half消息后續(xù)如何處理。
9、停一下腳本想想上面這個(gè)流程的意義在哪里?
看到這里我們來停下腳步想想,上面這個(gè)流程的意義在哪里呢?
其實(shí)很簡(jiǎn)單,如果你的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ù)庫(kù)失敗了,那么他也會(huì)進(jìn)行回滾,不會(huì)執(zhí)行后續(xù)流程了!
如果要是訂單系統(tǒng)收到half消息了,然后還更新自己數(shù)據(jù)庫(kù)成功了,訂單狀態(tài)是“已完成”了,此時(shí)就必然會(huì)發(fā)送commit請(qǐng)求給MQ,一旦消息commit了,那么必然保證紅包系統(tǒng)可以收到這個(gè)消息!
而且即使你commit請(qǐng)求發(fā)送失敗了,MQ也會(huì)有補(bǔ)償機(jī)制,回調(diào)你接口讓你判斷是否重新發(fā)送commit請(qǐng)求
總之,就是你的訂單系統(tǒng)只要成功了,那么必然要保證MQ里的消息是commit了可以讓紅包系統(tǒng)看到他!
所以大家可以結(jié)合我們的圖思考一下上述流程,通過這套事務(wù)消息的機(jī)制,是不是就可以保證我們的訂單系統(tǒng)一旦成功執(zhí)行了數(shù)據(jù)庫(kù)操作,就一定會(huì)通知到紅包系統(tǒng)去派發(fā)紅包?至少訂單系統(tǒng)到MQ之間的消息傳輸是不會(huì)有丟失的問題了!
當(dāng)前文章:深入探究RocketMQ事務(wù)機(jī)制的實(shí)現(xiàn)流程,為什么它能做到發(fā)送消息零丟失?
路徑分享:http://www.chinadenli.net/article28/gjoocp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、網(wǎng)站改版、商城網(wǎng)站、小程序開發(fā)、做網(wǎng)站、虛擬主機(jī)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)