基於MQ的非同步呼叫保證各服務間的分布式事務

2022-09-10 18:33:12 字數 3105 閱讀 6206

乙個服務傳送乙個訊息給 mq,即訊息中介軟體,比如 rocketmq、rabbitmq、kafka、activemq 等等。然後,另乙個服務從 mq 消費到一條訊息後進行處理。這就成了基於 mq 的非同步呼叫。

一、可靠訊息最終一致性方案的核心流程

1、上游服務投遞訊息

如果要實現可靠訊息最終一致性方案,一般可以自己寫乙個可靠訊息服務,實現一些業務邏輯。

首先,上游服務需要傳送一條訊息給可靠訊息服務。這條訊息一般就是對下游乙個服務介面的呼叫,裡面包含了對應的一些請求引數。

然後,可靠訊息服務就得把這條訊息儲存到自己的資料庫裡面取,狀態為「待確認」。

接著,上游服務就可以執行自己本地的資料庫操作,根據自己的執行結果,再次呼叫可靠訊息服務介面。

如果本地資料庫操作執行成功了,那麼就找可靠訊息服務確認那條訊息。如果本地資料庫操作失敗了,那麼就找可靠訊息服務刪除那條訊息。

此時如果是確認訊息,那麼可靠訊息服務就把資料庫裡的訊息狀態更新為「已傳送」,同時將訊息傳送給 mq。

這裡有乙個很關鍵的點,就是更新資料庫裡的訊息狀態和投遞訊息到 mq。這倆操作,需要放在乙個方法裡,且開啟本地事務。

1)如果資料庫更新訊息的狀態失敗了,那麼就拋異常退出,就別投遞到 mq。

2)如果投遞 mq 失敗報錯了,那麼就要拋異常讓本地資料庫事務回滾。

3)這倆操作必須一起成功,或者一起失敗。

2、下游服務接收訊息

下游服務就一直等著從 mq 消費訊息就好了,如果消費到了訊息,就操作本地資料庫。

如果操作成功了。就反過來通知可靠訊息服務,說自己處理成功了,然後可靠訊息服務就會把訊息狀態設定為「已完成」。

3、確保上游服務對訊息的100%可靠投遞

上面的流程的乙個問題是,如果在上述投遞訊息的過程中各個環節出現了問題該怎麼辦?

1)如果上游服務給訊息服務傳送待確認訊息的過程出錯來了,那上游服務感知到呼叫異常,就不會執行下面的流程了。

2)如果上游服務操作完本地資料庫之後,通知可靠訊息服務確認訊息或者刪除訊息的時候,出現了問題:比如沒通知成功,或者沒有執行很高,或者是可靠訊息伺服器沒成功的投遞到 mq。這些的情況下,可靠訊息服務的資料庫裡的狀態會一直是「待確認」。此時,我們可以在可靠訊息服務裡開發乙個後台定時執行的執行緒,不停的檢查各個訊息的狀態。如果一直是「待確認」狀態,就認為這個訊息出了點什麼問題。此時可以**上游服務提供的介面,問問這個老是這個狀態對應的資料執行成功沒有,如果是執行成功了就將訊息的傳送狀態改成「已傳送」,同時投遞訊息到 mq,不過這種情況下更多的可能是沒有執行成功,此時將可靠訊息服務將資料庫中的這條訊息刪除即可。

通過以上兩步,可以保證可靠訊息服務一定會嘗試完成訊息到 mq 的投遞。

4、保證下游服務對訊息的 100% 可靠接收

如果下游服務消費訊息時出了問題,沒有消費到,或者是下游服務對訊息的處理失敗了,怎麼辦?

其實也沒有關係,在可靠訊息服務裡開發乙個後台執行緒,不斷的檢查訊息狀態。如果訊息狀態一直是「已傳送」,始終沒有變成「已完成」,那就說明下游服務始終沒有處理成功。此時可靠訊息服務就可以再次嘗試重新投遞訊息到 mq,讓下游服務再次處理。只要下游服務的介面邏輯實現冪等性,保證多次處理乙個訊息,不會插入重複資料即可。

5、基於 mq 實現可靠訊息最終一致性方案

在上面的通用方案裡,完全依賴可靠訊息服務的各種自檢機制來確保:

1)如果上游服務的資料庫操作沒有成功,下游服務是不會收到任何通知的

2)如果上游服務的資料庫操作成功了,可靠訊息服務會確保將乙個呼叫訊息投遞給下游服務,而且一定會確保下游服務一定會成功處理這條訊息。

通過這套機制,保證了基於 mq 的非同步呼叫/通知的服務間的分布式事務保障。

其中,阿里開源的 rocketmq,就是實現了可靠訊息服務的所有功能,核心思想跟上面類似。只是 rocketmq 為了保證高併發、高可用、高效能,做了比較複雜的架構實現,非常優秀。。。

二、可靠訊息最終一致性方案的高可用保障生產實踐

實際落地生產的時候,如果沒有高併發場景的,完全可以參照上面的思路自己基於某個 mq 中介軟體開發乙個可靠訊息服務,如果有高併發場景的,可以用 rocketmq 的分布式事務支援,上面的那套流程都可以實現。

這套方案裡保障高可用性最大的乙個依賴點,就是 mq 的高可用。

任何一種 mq 中介軟體都有一整套的高可用保障機制,無論是 rabbitmq、rocketmq 還是 kafka。

如果 mq 集群整體故障,完全不可用時,就會導致業務系統的各個服務之間無法通過 mq 來投遞訊息,導致業務流程中斷。

mq 服務不可用時,服務降級怎麼做?

1、故障感知

比如,連續10次嘗試投遞 mq 都是異常出錯,網路無法連線等問題,說明 mq 故障不可用,此時觸發降級開關。然後根據降級開關來判斷這次是寫 mq 還是寫 redis。

2、基於kv儲存中佇列的降級方案

用 redis 作為訊息繼續投遞的替代品。

3、下游服務消費 mq 的降級感知

下游服務消費 mq 也是通過判斷降級開關是不是開啟了,來判斷自己是從 mq 消費資料還是從 redis 取資料。

4、故障恢復

如果降級開關開啟以後,需要每隔一段時間嘗試給 mq 投遞乙個訊息,以判斷其是否已經恢復。

如果 mq 已經恢復可以正常投遞訊息,此時就可以通過關閉降級開關,然後訊息繼續投遞到 mq,下游服務在確認 kv 儲存的各個佇列中已經沒有資料之後,就可以重新切換為從 mq 消費訊息。

5、更多

上面說的那套方案是通用的降級方案,具體可以根據業務特點來設計。

比如在投入 mq 時,盡可能的確保資料符合規範,可以被下游服務正確消費,否則下游服務會一直出錯。這個就像是 redis 的事務原理一樣,保證資料的正確性,那他就可以大概率的被成功執行,增加 redis 事務的成功率。

比如在服務降級時可以根據業務和**特點做開關,開關可以用 zookeeper 監聽,可以每次使用前從 redis 獲取降級開關值。

各種步驟,其實可以達到相同目的的,都可以 「平替」。

摘自於:

分布式系統原理 之7 基於MVCC的分布式事務

實現分布式事務除了使用類似 兩階段提交 協議等方式外,另一種簡單高效的方式就是使用mvcc multi version cocurrent control,多版本併發控制 技術 3 5 顧名思義,mvcc 即多個不同版本的資料實現併發控制的技術,其基本思想是為每次事務生成乙個新版本的資料,在讀資料時...

基於Redis的分布式服務限流

基於redis快取的分布式服務限流 1 定於限流註解,限流註解可以加需要限流的業務類上,value為限流業務型別 target elementtype.method retention retentionpolicy.runtime public inte ce tpscontrol2 限流配置類,...

如何保證分布式服務介面請求的順序

分布式系統介面的呼叫順序一般無需保證的,但有時候確實需要嚴格的順序保證。服務 a 呼叫服務 b,先插入再刪除。倆請求過去了,落在不同機器,可能插入請求因某些原因執行慢了一些,導致刪除請求先執行了,此時因為沒資料所以啥效果也沒。結果這時候插入請求過來了,資料插進去了!本該 先插入 再刪除 這條資料應該...