微服務分散式事務之LCN、TCC特點、事務補償機制緣由以及設計重點

等不到的口琴發表於2021-03-13

億級流量架構之分散式事務解決方案對比中, 已經簡單闡明瞭從本機事務到分散式事務的演變過程, 文章的最後簡單說明了TCC事務, 這兒將會深入瞭解TCC事務是原理, 以及理論支援, 最後會用Demo舉例實現。

XA協議

在上面提到的文章中, 分散式事務直接講二階段提交, 思維邏輯有些斷層, 但是那畢竟是比較解決方案, 在這兒從理論上推導分散式事務的根基, 也就是為什麼要二階段提交。

在單體應用中, 往往由自己來保證事務的一致性, 但是分散式中, 涉及到跨網路呼叫就難以保證, 從理論上講兩臺機器理論上無法達到一致的狀態, 所以專門從服務角色上將事務操作抽象出一個服務用來協調事務, 叫做協調者, 或者說事務管理者。由全域性事務管理器管理和協調的事務,可以跨越多個資源(如資料庫或JMS佇列)和程式。全域性事務管理器一般使用 XA 二階段提交協議與資料庫進行互動。

而XA協議, 就是事務管理者與各個服務模組(也叫服務者、資源管理者)之間的通訊遵守的協議就是XA協議, 簡單來說就是規範了介面, 這個協議由X/Open組織提出, 是分散式事務的規範。 XA規範主要定義了全域性事務管理器(TM)和區域性資源管理器(RM)之間的介面。除此之外, XA介面是雙向的系統介面,在事務管理器 (TM)以及一個或多個資源管理器(RM)之 間形成通訊橋樑,如上圖。

二階段提交協議

二階段協議,一句話說就是, 先進行一個複雜度低的詢問操作, 看看各個服務模組(也叫參與者、資源管理者、RM)是否可以進行事務操作, 一方面檢驗網路是否通暢, 另一方面看看對應的資源是否被佔用 , 如果可以得到的回應是所有的服務可以進行事務操作, 那麼這時候再通知所有服務提交事務。詳細的說, 二階段提交(2PC:Two-Phase Commit), 該協議將一個分散式的事務過程拆分成兩個階段: 投票事務提交 。為了讓整個資料庫叢集能夠正常的執行,該協議指定了一個 協調者(事務管理器) 單點,用於協調整個資料庫叢集各節點的執行。為了簡化描述,我們將資料庫叢集中的各個節點稱為 參與者(也叫服務者, 資源管理者)

第一階段:投票

該階段的主要目的在於打探資料庫叢集中的各個參與者是否能夠正常的執行事務,具體步驟如下:

  1. 協調者向所有的參與者傳送事務執行請求,並等待參與者反饋事務執行結果;
  2. 事務參與者收到請求之後,執行事務但不提交,並記錄事務日誌;
  3. 參與者將自己事務執行情況反饋給協調者,同時阻塞等待協調者的後續指令。

第二階段:事務提交

在經過第一階段協調者的詢盤之後,各個參與者會回覆自己事務的執行情況,這時候存在 3 種可能性:

  1. 所有的參與者都回復能夠正常執行事務。
  2. 一個或多個參與者回覆事務執行失敗。
  3. 協調者等待超時。

對於第 1 種情況,協調者將向所有的參與者發出提交事務的通知,具體步驟如下:

  1. 協調者向各個參與者傳送 commit 通知,請求提交事務;
  2. 參與者收到事務提交通知之後執行 commit 操作,然後釋放佔有的資源;
  3. 參與者向協調者返回事務 commit 結果資訊。

除此之外, 還有2種情況, 囿於篇幅, 詳情參考: 億級流量架構之分散式事務思路及方法後面的二階段提交協議

今天要聊的TCC就是二階段提交的具體事務實現。

LCN

詳情參考:官網(中文版)

有了前面的XA協議以及二階段提交的知識, 就不難理解LCN框架了, 這個框架可以理解成就是上面所說的協調者, 不生產事務, 只負責協調事務。5.0以後框架相容了LCN、TCC、TXC三種事務模式。

LCN中各個字母依次代表:鎖定事務單元(lock)、確認事務模組狀態(confirm)、通知事務(notify)。

在一個分散式系統下存在多個模組協調來完成一次業務。那麼就存在一次業務事務下可能橫跨多種資料來源節點的可能。TX-LCN目的是解決這樣的問題。

例如存在服務模組A 、B、 C。A模組是mysql作為資料來源的服務,B模組是基於redis作為資料來源的服務,C模組是基於mongo作為資料來源的服務。若需要解決他們的事務一致性就需要針對不同的節點採用不同的方案,並且統一協調完成分散式事務的處理。

在LCN中, 協調者稱之為TxManager , 參與者稱之為 TxClient, TxManager作為分散式事務的控制方, 事務發起方或者參與方都由TxClient端來控制決定。

時序圖(來源官網):

LCN核心步驟

  • 建立事務組
    是指在事務發起方開始執行業務程式碼之前先呼叫TxManager建立事務組物件,然後拿到事務標示GroupId的過程。
  • 加入事務組
    新增事務組是指參與方在執行完業務方法以後,將該模組的事務資訊通知給TxManager的操作。
  • 通知事務組
    是指在發起方執行完業務程式碼以後,將發起方執行結果狀態通知給TxManager,TxManager將根據事務最終狀態和事務組的資訊來通知相應的參與模組提交或回滾事務,並返回結果給事務發起方。

TCC

詳情參考: Github(中文版)

TCC事務機制相對於二階段提交,其特徵在於它不依賴資源管理器(RM)對XA協議的支援,而是通過對(由業務系統提供的)業務邏輯的排程來實現分散式事務, 將事務分成 Try 和 Confirm/ Cancel兩個階段。

三種操作作用: Try: 嘗試執行業務、 Confirm:確認執行業務、 Cancel: 取消執行業務。

整體流程如圖

Try 從執行階段來看,與傳統事務機制(二階段提交)中業務邏輯相同。但從業務角度來看,卻不一樣。TCC機制中的Try僅是一個初步操作,它和後續的確認一起才能真正構成一個完整的業務邏輯。TCC機制將傳統事務機制(2PC)中的業務邏輯一分為二:

拆分後保留的部分為初步操作(Try);

而分離出的部分即為驗證操作(Confirm/cancel),被延遲到事務提交階段執行。

三階段主要特點:

Try 階段 Confirm階段 Cancel階段
主要含義 嘗試執行業務 確認執行業務 取消執行業務
執行操作 完成所有業務檢查( 一致性 ) 不做任務業務檢查 釋放 Try 階段預留的業務資源
預留必須業務資源( 準隔離性 ) Confirm 操作滿足冪等性 Cancel 操作滿足冪等性
真正執行業務

TCC補償機制

在很多情況下,我們是無法做到強一致的 ACID 的。特別是我們需要跨多個系統的時候,而且這些系統還不是由一個公司所提供的。比如,在我們的日常生活中,我們經常會遇到這樣的情況,就是要找很多方協調很多事,而且要保證我們每一件事都成功,否則整件事就做不到。

比如,要出門旅遊, 我們需要幹這麼幾件事。

第一,向公司請假,拿到相應的假期;

第二,訂飛機票或是火車票;

第三,訂酒店;

第四,租車。

這四件事中,前三件必需完全成功,我們才能出行,而第四件事只是一個錦上添花的事,但第四件事一旦確定,那麼也會成為整個事務的一部分。這些事都是要向不同的組織或系統請求。我們可以並行地做這些事,而如果某個事有變化,其它的事都會跟著出現一些變化。

設想下面的幾種情況。

  1. 我沒有訂到返程機票,那麼我就去不了了。我需要把訂到的去程機票,酒店、租到的車都給取消了,並且把請的假也取消了。
  2. 如果我假也請好了,機票,酒店也訂好了,只是車沒租到,那麼並不影響我出行這個事,整個事還是可以繼續的。
  3. 如果我的飛機因為天氣原因取消或是晚點了,那麼我被迫要去調整和修改我的酒店預訂和租車的預訂。

從人類的實際生活當中,我們可以看出,上述的這些情況都是天天在發生的事情。所以,我們的分散式系統也是一樣的,也是需要處理這樣的事情——就是當條件不滿足,或是有變化的時候,需要從業務上做相應的整體事務的補償。

對於業務補償來說,首先需要將服務做成冪等性的,如果一個事務失敗了或是超時了,我們需要不斷地重試,努力地達到最終我們想要的狀態。然後,如果我們不能達到這個我們想要的狀態,我們需要把整個狀態恢復到之前的狀態。另外,如果有變化的請求,我們需要啟動整個事務的業務更新機制。

業務補償機制特點

由上可知,一個好的業務補償機制需要做到下面這幾點。

  1. 要能清楚地描述出要達到什麼樣的狀態(比如:請假、機票、酒店這三個都必須成功,租車是可選的),以及如果其中的條件不滿足,那麼,我們要回退到哪一個狀態。這就是所謂的整個業務的起始狀態定義。
  2. 當整條業務跑起來的時候,我們可以序列或並行地做這些事。對於旅遊訂票是可以並行的,但是對於網購流程(下單、支付、送貨)是不能並行的。總之,我們的系統需要努力地通過一系列的操作達到一個我們想要的狀態。如果達不到,就需要通過補償機制回滾到之前的狀態。這就是所謂的狀態擬合
  3. 對於已經完成的事務進行整體修改,可以考慮成一個修改事務。

其實,在純技術的世界裡也有這樣的事。比如,線上運維繫統需要釋出一個新的服務或是對一個已有的服務進行水平擴充套件,我們需要先找到相應的機器,然後初始化環境,再部署上應用,再做相應的健康檢查,最後接入流量。這一系列的動作都要完全成功,所以,我們的部署系統就需要管理好整個過程和相關的執行狀態。

業務補償的設計重點

業務補償主要做兩件事。

  1. 努力地把一個業務流程執行完成。
  2. 如果執行不下去,需要啟動補償機制,回滾業務流程。

所以,下面是幾個重點。

  • 因為要把一個業務流程執行完成,需要這個流程中所涉及的服務方支援冪等性。並且在上游有重試機制。
  • 我們需要小心維護和監控整個過程的狀態,所以,千萬不要把這些狀態放到不同的元件中,最好是一個業務流程的控制方來做這個事,也就是一個工作流引擎。所以,這個工作流引擎是需要高可用和穩定的。這就好像旅行代理機構一樣,我們把需求告訴它,它會幫我們搞定所有的事。如果有問題,也會幫我們回滾和補償的。
  • 補償的業務邏輯和流程不一定非得是嚴格反向操作。有時候可以並行,有時候,可能會更簡單。總之,設計業務正向流程的時候,也需要設計業務的反向補償流程。
  • 我們要清楚地知道,業務補償的業務邏輯是強業務相關的,很難做成通用的。
  • 下層的業務方最好提供短期的資源預留機制。就像電商中的把貨品的庫存預先佔住等待使用者在 15 分鐘內支付。如果沒有收到使用者的支付,則釋放庫存。然後回滾到之前的下單操作,等待使用者重新下單。

站在巨人的肩膀上

  1. 陳皓 左耳聽風專欄

相關文章