開發

深入探究 RocketMQ 事務機制的實現流程,為什么它能做到發送消息零丟失?

1、解決消息丟失的第一個問題:訂單系統推送消息領丟失

既然我們已經明確了消息在基于MQ傳輸的過程中可能丟失的幾個地方,那么我們接著就得一步一步考慮如何去解決各個環節丟失消息的問題,首先要解決的第一個問題,就是訂單系統推送消息到MQ的過程中,可能消息就丟失了。

之前我們也說過了,可能在訂單系統推送消息到MQ的過程中,就因為常見的網絡故障之類的問題,導致消息就丟失了,這里我們可以看一下下圖中的示意。            而在RocketMQ中,有一個非常強悍有力的功能,就是事務消息的功能,憑借這個事務級的消息機制,就可以讓我們確保訂單系統推送給出去的消息一定會成功寫入MQ里,絕對不會半路就搞丟了。

今天我們就來系統的分析一下RocketMQ的事務消息機制的原理。

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

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

可能有的朋友會覺得,訂單系統不就是先在自己數據庫里做一些增刪改操作,然后就直接發個消息到MQ去,讓其他關注這個訂單支付成功消息的系統去從MQ獲取消息做對應的處理就可以了么?

事實上還真不是這么簡單。

在基于RocketMQ的事務消息機制中,我們首先要讓訂單系統去發送一條half消息到MQ去,這個half消息本質就是一個訂單支付成功的消息,只不過你可以理解為他這個消息的狀態是half狀態,這個時候紅包系統是看不見這個half消息的,然后我們去等待接收這個half消息寫入成功的響應通知

我們看下面的圖

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

大家先別著急,你可以想一下,假設你二話不說讓訂單系統直接做了本地的數據庫操作,比如訂單狀態都更新為了已完成,然后你再發送消息給MQ,結果報出一堆異常,發現MQ掛了。

這個時候,必然導致你沒法通過消息通知到紅包系統去派發紅包,那用戶一定會發現自己訂單支付了,結果紅包沒收到。

所以,在這里我們首先第一件事,不是先讓訂單系統做一些增刪改操作,而是先發一個half消息給MQ以及收到他的成功的響應,初步先跟MQ做個聯系和溝通

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

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

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

可能你發現報錯了,可能MQ就掛了,或者這個時候網絡就是故障了,所以導致你的half消息都沒發送成功,總之你現在肯定沒法跟MQ通信了。

這個時候你的訂單系統就應該執行一系列的回滾操作,比如對訂單狀態做一個更新,讓狀態變成“關閉交易”,同時通知支付系統自動進行退款,這才是正確的做法

因為你訂單雖然支付了,但是包括派發紅包、發送優惠券之類的后續操作是無法執行的,所以此時必然應該把錢款退還給用戶,說交易失敗了。

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

結果等了一會兒,系統顯示后臺系統有異常,交易失敗了,然后過了一會兒就讓支付寶自動退款給我了。

其實這就是類似的例子。

4、half消息成功之后,訂單系統完成自己的任務

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

這時你的訂單系統就應該在自己本地的數據庫里執行一些增刪改操作了,因為一旦half消息寫成功了,就說明MQ肯定已經收到這條消息了,MQ還活著,而且目前你是可以跟MQ正常溝通的。

我們看下面的圖,示意了下一步是訂單系統執行自己的增刪改操作。 

5、如果訂單系統的本地事務執行失敗了怎么辦?

接著我們繼續看下一種情況,萬一訂單系統更新自己的數據庫失敗了怎么辦?

比如訂單系統的數據庫當時也有網絡異常,或者數據庫掛了,總而言之,就是你想把訂單更新為“已完成”這個狀態,是干不成了。

這個時候其實也很簡單,直接就是讓訂單系統發送一個rollback請求給MQ就可以了

這個意思就是說,你可以把之前我發給你的half消息給刪除掉了,因為我自己這里都出問題了,已經無力跟你繼續后續的流程了。

我們看下面的圖,我給出了這個示意

當然你發送rollback請求給MQ刪除那個half消息之后,你的訂單系統就必須走后續的回退流程了,就是通知支付系統退款。

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

然后你可以開一個后臺線程在MySQL數據庫恢復之后 ,再把訂單狀態更新為“已關閉”。不過這個不在我們討專欄的范圍之內。

6、如果訂單系統完成了本地事務之后,接著干什么?

如果訂單系統成功完成了本地的事務操作,比如把訂單狀態都更新為“已完成”了,此時你就可以發送一個commit請求給MQ,要求讓MQ對之前的half消息進行commit操作,讓紅包系統可以看見這個訂單支付成功消息

我們看下面的圖

之前我們也提到過了,所謂的half消息實際就是訂單支付成功的消息,只不過他的狀態是half

也就是說,他是half狀態的時候,紅包系統是看不見他的,沒法獲取到這條消息。必須等到訂單系統執行commit請求,消息被commit之后,紅包系統才可以看到和獲取這條消息進行后續處理。

7、讓流程嚴謹一些:如果發送half消息成功了,但是沒收到響應呢?

大致的事務消息的流程是講完了,但是接著讓我們來進行比較嚴謹的分析

如果我們是把half消息發送給MQ了,MQ給保存下來了,但是MQ返回給我們的響應我們沒收到呢?此時會發生什么事情?

這個時候我們沒收到響應,可能就會網絡超時報錯,也可能直接有其他的異常錯誤,這時訂單系統會誤以為是發送half消息到MQ失敗了,訂單系統就直接會執行退款流程了,訂單狀態也會標記為“已關閉”。

我們看下面的圖的示意

但是這時MQ已經存儲下來一條half消息了,那對這個消息怎么處理?

其實RocketMQ這里有一個補償流程,他會去掃描自己處于half狀態的消息,如果我們一直沒有對這個消息執行commit/rollback操作,超過了一定的時間,他就會回調你的訂單系統的一個接口,問問你說,這個消息到底怎么回事?

你到底是打算commit這個消息還是要rollback這個消息?

我們看下圖示意

這個時候我們的訂單系統就得去查一下數據庫,看看這個訂單當前的狀態,一下發現訂單狀態是“已關閉”,此時就知道,你必然是得發送rollback請求給MQ去刪除之前那個half消息了!

我們看下圖

8、如果rollback或者commit發送失敗了呢?

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

結果因為網絡故障,導致rollback或者commit請求發送失敗了呢?

這個時候其實也很簡單,因為MQ里的消息一直是half狀態,所以他過了一定的超時時間會發現這個half消息有問題,他會回調你的訂單系統的接口

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

本質這個MQ的回調就是一個補償機制,就怕你的half消息響應沒收到,或者rollback、commit請求沒發送成功,所以他會來找你問問對half消息后續如何處理。

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

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

其實很簡單,如果你的MQ有問題或者網絡有問題,half消息根本都發不出去,此時half消息肯定是失敗的,那么訂單系統就不會執行后續流程了!

如果要是half消息發送出去了,但是half消息的響應都沒收到,然后執行了退款流程,那MQ會有補償機制來回調找你詢問要commit還是rollback,此時你選擇rollback刪除消息就可以了,不會執行后續流程!

如果要是訂單系統收到half消息了,結果訂單系統自己更新數據庫失敗了,那么他也會進行回滾,不會執行后續流程了!

如果要是訂單系統收到half消息了,然后還更新自己數據庫成功了,訂單狀態是“已完成”了,此時就必然會發送commit請求給MQ,一旦消息commit了,那么必然保證紅包系統可以收到這個消息!

而且即使你commit請求發送失敗了,MQ也會有補償機制,回調你接口讓你判斷是否重新發送commit請求

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

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

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

我還沒有學會寫個人說明!

公布!達摩院2020年十大科技趨勢

上一篇

微服務架構的四大殺手锏

下一篇

你也可能喜歡

深入探究 RocketMQ 事務機制的實現流程,為什么它能做到發送消息零丟失?

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃
天津11选五开奖结果手 江苏11选5一定牛预测 三肖中特期期准+资料 必出一期香港 主流互联网理财平台 安装彩库宝典最新开奖 2020大类资产配置 开元棋牌app下载安装 排列三开机号 15选5百分百中奖技巧 平特三连肖赔率