解決業務代碼里的分散式事務一致性問題

原文標題:一種提高微服務架構的穩定性與數據一致性的方法

微服務架構解決了很多問題,但是同時引入了很多問題。本文要探討的是如何解決下面這幾個問題。

有大量的同步 RPC 依賴,如何保證自身的可靠性?

依賴的微服務調用失敗了,我應該失敗,還是成功。依賴很多外部服務之後,自身如何保障穩定性。如果所有依賴的服務成功,我才算成功,自身的穩定性就堪憂了。

RPC 調用失敗,降級處理之後如何保證數據可修復?

如果調用失敗時,選擇跳過。那麼因此產生的數據不一致性問題如何修復?平時毛毛雨,可以忽略。但是大故障之後,人工還是要來擦屁股的,這個成本就特別高。使用消息隊列的最大的意義是在讓消息可以在故障的時候堆積起來,等故障恢復了再慢慢來處理,減少人工介入的成本。

消息隊列是一個RPC主流程的旁路流程,怎麼保證可靠性?

依賴消息隊列做系統解耦的時候,怎麼確保消息自身是可靠入隊列的?消息是否需要先可靠寫入隊列,然後再提交資料庫事務?如果消息必須先寫入隊列,比如 kafka。但是 kafka 掛了怎麼辦?那我在線業務豈不被離線的隊列給連累了?

消息隊列怎麼保持與資料庫的事務一致?

如果消息是先寫入隊列,然後資料庫提交事務。那麼就會有因為並發修改的情況下,資料庫提交失敗,但是消息已經寫入到隊列的情況。如果隊列後面掛了獎勵等業務流程,這個時候就會導致錯發,或者要求獎勵那邊去再查一遍資料庫的狀態。但是如果先提交資料庫事務,後寫入隊列,又無法嚴格保證隊列里的消息是沒有丟失的。

這些問題是所有混用了 RPC 和非同步隊列的業務都會遇到的普遍問題。這裡我給一個提案來解決以上的所有問題。

同步轉非同步,解決穩定性問題

在平時的時候,都是 RPC 同步調用。如果調用失敗了,則自動把同步調用降級為非同步的。消息此時進入隊列,然後非同步被重試。所以處理下游依賴就變成了三種可能性

  • 完全強依賴,下游不能掛
  • 因為我的返回值依賴了某個下游的處理結果,我必須同步調用它。但是不是強依賴,可降級。降級時不返回這部分的數據。同步調用降級時轉為非同步的。
  • 完全非同步化。下游服務只是消費我寫入的隊列,我不與之直接RPC通信

把消息隊列放入到主流程

如果要把重要的業務邏輯掛在消息隊列後面。必須要保證消息隊列里的數據的完整性,不能有丟失的情況。所以不能是把消息隊列的寫入作為一個旁路的邏輯。如果消息隊列寫入失敗或者超時,都應該直接返回錯誤,而不是允許繼續執行。

Kafka 的穩定性和延遲時常不能滿足在線服務的需要。比如如果要可靠寫入三副本,Kafka 需要等待多個 broker 的應答,這個延遲可能會有比較大的波動。在無法及時寫入的情況,我們需要使用本地文件充當一個緩衝。實際上是通過引入本地文件隊列結合遠程分散式隊列構成一個可用性更高,延遲更低的組合隊列方案。這個本地的隊列如果能封裝到一個 Kafka 的 Agent 作為本地寫入的代理,那是最理想的實現方式。

保障分散式事務一致性

前面我們說了要用隊列,要依靠隊列。但是並沒有解決資料庫和消息隊列是兩個獨立的事務,沒法保證最終一致性的問題。

這麼一串流程,如果RPC1,RPC2成功了,但是流程被中斷了(比如停電)。那麼RPC3就不會被執行,數據就不一致了。這裡的RPC1可能是資料庫操作,RPC3可能是消息隊列的入隊列的調用,都是一回事。

這個問題的解決辦法是把責任推給第三方。

我們需要一個延遲隊列,在業務入口的時候掛一個延遲job,然後執行完了取消它。如果沒有執行完,則延遲隊列負責去觸發這個延遲任務,把整個業務流程重複執行一遍。

這樣我們就可以保證任意rpc操作流程的最終一致性了。而入kafka消息隊列作為RPC操作的一種,自然也是可以得到保證的了。

總結

前面給了三個獨立的技術方案

  • 使用同步轉非同步的方案,提高同步 RPC 的可用性,同時提高數據一致性。
  • 引入本地隊列作為兜底,提高消息隊列的總體可用性,以及降低延遲。
  • 通過引入延遲隊列做監管方,保證一串RPC調用可以最終被執行完

我們只需要把這三個獨立的方案結合到一起,就可以把隊列技術應用到純 RPC 同步組合的微服務集群里,用於提高可用性和數據的一致性。同時可以保證這份消息數據是可靠的,從而給其他的業務邏輯把自己放在隊列後面,建立了前提條件。


推薦閱讀:

TiKV為什麼用一個單點的授時服務而不是用一致性集群來授時呢?
微服務如何處理分散式事務?
詳解CockroachDB事務處理系統
常用的分散式事務解決方案介紹有多少種?
如何理解阿里大神程立的分散式事務文檔?

TAG:微服务架构 | 异步 | 分布式事务 |