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

石杉的架構筆記發表於2022-12-08

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

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

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

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

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

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

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

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

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

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

我們看下面的圖

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

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

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

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

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

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

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

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

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

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

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

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

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

其實這就是類似的例子。

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

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

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

我們看下面的圖,示意了下一步是訂單系統執行自己的增刪改操作。 深入探究 RocketMQ 事務機制的實現流程,為什麼它能做到傳送訊息零丟失?

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

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

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

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

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

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

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

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

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

然後你可以開一個後臺執行緒在MySQL資料庫恢復之後 ,再把訂單狀態更新為“已關閉”。不過這個不在我們討專欄的範圍之內。

6、如果訂單系統完成了本地事務之後,接著幹什麼?

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

我們看下面的圖

深入探究 RocketMQ 事務機制的實現流程,為什麼它能做到傳送訊息零丟失?之前我們也提到過了,所謂的half訊息實際就是訂單支付成功的訊息,只不過他的狀態是half

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

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

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

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

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

我們看下面的圖的示意

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

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

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

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

我們看下圖示意

深入探究 RocketMQ 事務機制的實現流程,為什麼它能做到傳送訊息零丟失?這個時候我們的訂單系統就得去查一下資料庫,看看這個訂單當前的狀態,一下發現訂單狀態是“已關閉”,此時就知道,你必然是得傳送rollback請求給MQ去刪除之前那個half訊息了!

我們看下圖

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

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之間的訊息傳輸是不會有丟失的問題了!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901780/viewspace-2671709/,如需轉載,請註明出處,否則將追究法律責任。

相關文章