四種基于MQ的分布式事務(wù)解決方案

在微服務(wù)的時代,分布式事務(wù)是繞不開的話題,盡管在大多數(shù)場景下,我們并不需要使用分布式事務(wù),但是 不需要使用 不代表 可以不會使用,萬一哪天真需要用到了呢?分布式事務(wù)是一個比較大的話題,今天我們來看看基于MQ的分布式事務(wù)解決方案。

在實際開發(fā)中,為了簡化分布式事務(wù),我們和其他服務(wù)交互,經(jīng)常會采用MQ的方式,我們先來看下如果采用MQ的方式和其他服務(wù)進行交互,應(yīng)該怎么做。

采用MQ的方式和其他服務(wù)進行交互

基于RocketMQ 事務(wù)消息+最大努力通知

RocketMQ提供了事務(wù)的消息的功能,我們來看下事務(wù)消息的原理:



Producer發(fā)送一個半消息到Broker;

Broker收到半消息后,響應(yīng)Producer,Broker會將半消息存儲到一個特殊的Topic,此時Consumer是不能消費此消息的;

Producer執(zhí)行本地事務(wù);

Producer根據(jù)本地事務(wù)的執(zhí)行情況,告知Broker Commit或者Rollback,如果Commit,Broker會將消息投遞到正常的Topic,此時Consumer可以正常消費此消息。

如果因為某種原因,Broker未收到Producer的Commit或者Rollback,Broker會發(fā)起回查,Producer收到回查請求后,根據(jù)本地事務(wù)狀態(tài),重新響應(yīng)Broker Commit或者Rollback。

對于Producer而言,本地事務(wù)和發(fā)送消息是一致的,要么都成功,要么都失敗,但是無法保證Consumer的一致性,有不少人稱之為“單端事務(wù)”,但是RocketMQ還為Consumer提供了重試的功能,只要Consumer不返回消息消費成功,Consumer還有16次機會可以重新消費此消息,執(zhí)行自身的業(yè)務(wù)操作,相當于最大努力通知。

這個方案可以說是最常用的分布式事務(wù)解決方案了,不管是實現(xiàn),還是原理都比較簡單,但是仔細想想此方案還是有缺點的:

和RocketMQ強綁定,因為只有RocketMQ才提供了完善的事務(wù)消息的功能;

降低了可用性,如果Producer發(fā)送半消息失敗,流程就終止了;

代碼侵入性強,Producer需要提供執(zhí)行本地事務(wù)、回調(diào)兩個方法。

基于本地消息表

此方案在RocketMQ事務(wù)消息推出之前,是采用較多的一個分布式事務(wù)解決方案,需要在庫中新建一張本地消息表,此表有如下核心字段:

topic:消息需要發(fā)送到哪個topic;

state:消息狀態(tài),有三種狀態(tài):1.未發(fā)送 2.發(fā)送失敗 3.發(fā)送成功;

retry_num:重試次數(shù);

time:消息產(chǎn)生的時間;

last_retry_time:最近重試時間;

message:消息內(nèi)容。

作為Producer來說,原本是執(zhí)行完本地業(yè)務(wù)后,直接將消息投遞到MQ,現(xiàn)在需要將消息保存至本地消息表,然后由定時任務(wù)讀取本地消息表,將需要推送的消息投遞到MQ,具體做法如下:

本地業(yè)務(wù)+插入本地消息表 組成一個大的本地事務(wù),以此保證兩者的原子性、一致性,本地業(yè)務(wù)+插入本地消息表兩個操作要么同時成功,要么同時失?。?br>
新增定時任務(wù),不斷的掃描本地消息表,將未成功投遞的消息進行投遞,投遞成功,修改本地消息表的狀態(tài)字段,投遞失敗,修改本地消息表中的狀態(tài),并且重試次數(shù)+1,等待下一次重新投遞。

這個方案實現(xiàn)也非常簡單,缺點也顯而易見:

嚴重依賴Job;

及時性比較差,如果Job每10分鐘運行一次,那可能就有10分鐘的延遲。如果Job每5分鐘運行一次,那可能就有5分鐘的延遲。

不斷的掃描本地消息表,對數(shù)據(jù)庫也是一種壓力;

需要定期清理本地消息表。

基于內(nèi)存隊列+本地消息表

本地消息表這個方案還是不錯的,有沒有辦法改善它的缺點呢?當然有,這個方案就是對傳統(tǒng)的本地消息表方案進行了改造,據(jù)說部分二三線互聯(lián)網(wǎng)公司就是采用的此種方案,具體看圖(如果圖片看不清,可以將圖片下載到本地 或者 在新標簽頁中查看圖片):



 雖然此方案需要自己編碼實現(xiàn),但是整體來說,編碼難度不大。

不管是基于RocketMQ事務(wù)消息的分布式事務(wù)解決方案,還是基于本地消息表的分布式事務(wù)解決方案,還是基于內(nèi)存隊列+本地消息表的分布式事務(wù)解決方案,都有消息表的概念,只是消息表的具體存在形式不同,一個是以數(shù)據(jù)表的形式存在(存在了數(shù)據(jù)庫),一個是以Topic的形式存在(存在了Broker)。

以上三種方案都有一個局限性:和其他服務(wù)進行交互,必須比較采用MQ的方式。

不采用MQ的方式和其他服務(wù)進行交互

基于RocketMQ的延遲消息檢查方案

上述三個方案,都有一個局限性:和其他服務(wù)進行交互,必須采用MQ的方式,如果不滿足這個條件,如何采用MQ的方式來實現(xiàn)分布式事務(wù)呢:采用基于RocketMQ的延遲消息檢查方案。

Producer發(fā)送延遲消息;

其他服務(wù)需要提供檢查接口,重試或者回滾接口;

Producer收到延遲消息后,檢查自身的業(yè)務(wù)操作是否執(zhí)行成功,根據(jù)具體情況,判斷是否需要重試或回滾,然后調(diào)用其他服務(wù)提供的檢查接口,檢查其他服務(wù)的業(yè)務(wù)操作是否執(zhí)行成功,再根據(jù)具體的情況判斷是否調(diào)用其他服務(wù)提供的重試、回滾接口。

此方案實現(xiàn)也比較簡單,且容易理解,但是也有缺點:

和RocketMQ強綁定,因為只有RocketMQ才提供了完善的延遲消息的功能;

降低了可用性,如果Producer發(fā)送延遲消息失敗,流程就終止了。

作者:碼出宇宙


歡迎關(guān)注微信公眾號 :碼出宇宙