當前位置:首頁 > IT技術 > Web編程 > 正文

.Net Core with 微服務 - 分布式事務 - 可靠消息最終一致性
2021-09-23 06:53:20

前面我們講了分布式事務的2PC、3PC , TCC 的原理。這些事務其實都在盡力的模擬數(shù)據(jù)庫的事務,我們可以簡單的認為他們是一個同步行的事務。特別是 2PC,3PC 他們完全利用數(shù)據(jù)庫的事務能力,在一階段開始事務后不進提交會嚴重影響應用程序的并發(fā)性能。TCC 一階段雖然不會阻塞數(shù)據(jù)庫,但是它同樣是在盡力追求同時成功同時失敗的一致性要求。但是在很多時候,我們的應用程序的核心業(yè)務為了追求更高的性能、更高的可用性,可以允許在一段時間內的數(shù)據(jù)不一致性,只需要在最終時刻數(shù)據(jù)是一致就可以了?;谝陨蠄鼍拔覀兛梢圆捎没诳煽肯⒎盏淖罱K一致性分布式事務處理方案。

總體架構


可靠消息最終一致性的架構上分3個部分:

  1. 主動方:主動發(fā)起事務的一方,一般是指分布式事務中最先開始執(zhí)行的那個服務,也是核心業(yè)務服務
  2. 可靠消息服務:可靠消息服務由關系型數(shù)據(jù)庫、消息隊列MQ等組件組成,利用關系型數(shù)據(jù)庫的強一致性特性來持久化消息的狀態(tài),利用MQ來保證消息的可靠投遞及消費
  3. 被動方:被動方訂閱MQ的消息,當收到MQ的消息后執(zhí)行對應的業(yè)務
    以上是比較粗狂的結構圖,下面我們來詳細分析一下這個事務的執(zhí)行過程。

流程


該方案總體流程上可分為以下步驟:

  1. 主動方在真正的業(yè)務開始前先向可靠消息服務發(fā)送一個“待確認”的消息
  2. 可靠消息服務收到待確認消息后持久化消息到數(shù)據(jù)庫
  3. 如果以上操作成功則主動方開始真正的業(yè)務,如果失敗則直接放棄執(zhí)行業(yè)務
  4. 如果業(yè)務執(zhí)行成功則發(fā)送“確認”消息給可靠消息服務,如果執(zhí)行失敗則發(fā)送“取消”給可靠消息服務。
  5. 如果可靠消息服務收到“確認”消息則更新數(shù)據(jù)庫里的消息記錄的狀態(tài)為“待發(fā)送”,如果收到的消息為“取消”則更新消息狀態(tài)為“已取消”
  6. 如果上一步更新的數(shù)據(jù)庫為“待發(fā)送”,那么會開始往MQ投遞消息,并且更改數(shù)據(jù)庫里的消息記錄的狀態(tài)為“已發(fā)送”
  7. 上一步往MQ投遞消息成功后,MQ會給被動方推送消息。
  8. 被動方收到消息后開始處理業(yè)務
  9. 如果業(yè)務處理成功,則被動方對MQ進行ACK回復,則這條消息會從MQ內移除掉
  10. 如果業(yè)務處理成功,則發(fā)送“已完成”消息給可靠消息服務
  11. 可靠消息服務收到“已完成”消息后更新數(shù)據(jù)庫消息記錄未“已完成”

異常處理

以上我們描述的是一套在理想情況下執(zhí)行的邏輯。但是分布式系統(tǒng)由于網絡的存在,網絡的不可靠性會導致我們消息的傳遞沒辦法100%成功。我們的可靠消息服務跟主動方、被動方之間的交互也是分布式的,這就需要我們在流程上有很多補償?shù)臋C制。以下我們來討論一些異常情況:

  1. 如果步驟1發(fā)送“待確認”消息失敗,主動方業(yè)務不會執(zhí)行,直接放棄事務,不會有影響
  2. 如果步驟1發(fā)送“待確認”消息成功,并且可靠消息已經更新“待確認”成功,但是由于網絡問題,比如超時,主動方得到的結果是失敗,主動方會放棄執(zhí)行事務,標記為“已取消”。這個時候就會出現(xiàn)可靠消息服務跟主動方的狀態(tài)出現(xiàn)不一致的情況。
    為解決這個問題,我需要主動方提供一個事務狀態(tài)查詢接口,可靠消息服務這邊則啟動一個定時任務,定時去查這些長時間處于待確認的事務,然后通過主動方的接口確認這些事務是已執(zhí)行,還是已取消。
  3. 如果步驟4,主動方發(fā)送“確認”消息失敗,可靠消息服務會通過定時任務通過主動方的查詢接口去確認狀態(tài)。
  4. 步驟6,投遞消息給MQ跟更新狀態(tài)為“已發(fā)送”,是這個流程中至關重要的一步。理想的流程是整個2個操作同時成功同時失敗,保持強一致性。網上很多文章都會說“為了保證發(fā)送MQ消息跟更新消息狀態(tài)同時成功同時失敗,需要把這個2個步驟寫同一個本地事務中”。這是完全不靠譜的,數(shù)據(jù)庫跟MQ本就是2個獨立的服務,如果通過一個本地的事務就能保證一致性,那么我們現(xiàn)在討論的分布式事務毫無意義,直接寫在一個本地事務里不就完了么。
    寫在本地事務內只能盡可能的保證數(shù)據(jù)庫更新跟MQ投遞消息同時成功,但是并不能保證100%一致。
    以下我們來分2種情況分析失敗的情況:
事務開始
1. send to mq
2. database update
事務結束

先發(fā)送 MQ 消息,可能 MQ 消息發(fā)送成功,但是database由于某些原因更新失敗了。數(shù)據(jù)庫可以回滾,但是通常的MQ沒有回滾能力。

事務開始
1. database update
2. send to mq
事務結束

先更新數(shù)據(jù)庫,再發(fā)送 MQ 消息,同樣會有問題。就算1,2都執(zhí)行成功了,但是事務是需要提交的,數(shù)據(jù)庫有可能在提交階段失敗,數(shù)據(jù)庫是可以回滾,但是 MQ 的消息已經發(fā)出去了,它并沒有回滾的能力。
為了解決這個問題,我們同樣需要補償機制。在可靠消息服務一側開啟定時任務,定時去查詢那些長期處于“待發(fā)送”的事務,再次對 MQ 進行投遞消息。這個機制有可能造成被動方重復收到 MQ 的消息,這就需要被動方處理業(yè)務的時候要進行冪等處理。

總結

通過以上我們詳細介紹了可靠消息最終一致性事務解決方案的總體結構跟執(zhí)行的流程,以及對異常情況的一些補償方法,總體流程上還是比較清晰簡單的。但是可靠消息最終一致性方案在使用上也是具有比較強的局限性,因為它的異步特性跟有可能出現(xiàn)的高延時性不適合處理一些敏感業(yè)務。比如它適合處理消費新增積分場景,但是不合適處理積分兌換禮品的場景。因為如果積分扣減延遲了,那么用戶就可能兌換超出本身積分多的多的禮品。所以我們選擇分布式事務的時候還需根據(jù)場景來進行選擇。
好了講了這么多分布式事務的原理,下一期我們使用 .NET 真正的實現(xiàn)一個分布式事務,敬請期待。

.Net Core with 微服務 - 什么是微服務
.Net Core with 微服務 - 架構圖
.Net Core with 微服務 - Ocelot 網關
.Net Core with 微服務 - Consul 注冊中心
.Net Core with 微服務 - Seq 日志聚合
.Net Core with 微服務 - Elastic APM
.Net Core with 微服務 - Consul 配置中心
.Net Core with 微服務 - Polly 熔斷降級
.Net Core with 微服務 - 分布式事務 - 2PC、3PC
.Net Core with 微服務 - 分布式事務 - TCC

關注我的公眾號一起玩轉技術

本文摘自 :https://www.cnblogs.com/

開通會員,享受整站包年服務立即開通 >