在億級流量架構之分布式事務解決方案對比中, 已經簡單闡明了從本機事務到分布式事務的演變過程, 文章的最后簡單說明了TCC事務, 這兒將會深入了解TCC事務是原理, 以及理論支持, 最后會用Demo舉例實現。
XA協議
在上面提到的文章中, 分布式事務直接講二階段提交, 思維邏輯有些斷層, 但是那畢竟是比較解決方案, 在這兒從理論上推導分布式事務的根基, 也就是為什么要二階段提交。
在單體應用中, 往往由自己來保證事務的一致性, 但是分布式中, 涉及到跨網絡調用就難以保證, 從理論上講兩台機器理論上無法達到一致的狀態, 所以專門從服務角色上將事務操作抽象出一個服務用來協調事務, 叫做協調者, 或者說事務管理者。由全局事務管理器管理和協調的事務,可以跨越多個資源(如數據庫或JMS隊列)和進程。全局事務管理器一般使用 XA 二階段提交協議與數據庫進行交互。
而XA協議, 就是事務管理者與各個服務模塊(也叫服務者、資源管理者)之間的通訊遵守的協議就是XA協議, 簡單來說就是規范了接口, 這個協議由X/Open組織提出, 是分布式事務的規范。 XA規范主要定義了全局事務管理器(TM)和局部資源管理器(RM)之間的接口。除此之外, XA接口是雙向的系統接口,在事務管理器 (TM)以及一個或多個資源管理器(RM)之 間形成通信橋梁,如上圖。
二階段提交協議
二階段協議,一句話說就是, 先進行一個復雜度低的詢問操作, 看看各個服務模塊(也叫參與者、資源管理者、RM)是否可以進行事務操作, 一方面檢驗網絡是否通暢, 另一方面看看對應的資源是否被占用 , 如果可以得到的回應是所有的服務可以進行事務操作, 那么這時候再通知所有服務提交事務。詳細的說, 二階段提交(2PC:Two-Phase Commit), 該協議將一個分布式的事務過程拆分成兩個階段: 投票 和 事務提交 。為了讓整個數據庫集群能夠正常的運行,該協議指定了一個 協調者(事務管理器) 單點,用於協調整個數據庫集群各節點的運行。為了簡化描述,我們將數據庫集群中的各個節點稱為 參與者(也叫服務者, 資源管理者) 。
第一階段:投票
該階段的主要目的在於打探數據庫集群中的各個參與者是否能夠正常的執行事務,具體步驟如下:
- 協調者向所有的參與者發送事務執行請求,並等待參與者反饋事務執行結果;
- 事務參與者收到請求之后,執行事務但不提交,並記錄事務日志;
- 參與者將自己事務執行情況反饋給協調者,同時阻塞等待協調者的后續指令。
第二階段:事務提交
在經過第一階段協調者的詢盤之后,各個參與者會回復自己事務的執行情況,這時候存在 3 種可能性:
- 所有的參與者都回復能夠正常執行事務。
- 一個或多個參與者回復事務執行失敗。
- 協調者等待超時。
對於第 1 種情況,協調者將向所有的參與者發出提交事務的通知,具體步驟如下:
- 協調者向各個參與者發送 commit 通知,請求提交事務;
- 參與者收到事務提交通知之后執行 commit 操作,然后釋放占有的資源;
- 參與者向協調者返回事務 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 的。特別是我們需要跨多個系統的時候,而且這些系統還不是由一個公司所提供的。比如,在我們的日常生活中,我們經常會遇到這樣的情況,就是要找很多方協調很多事,而且要保證我們每一件事都成功,否則整件事就做不到。
比如,要出門旅游, 我們需要干這么幾件事。
第一,向公司請假,拿到相應的假期;
第二,訂飛機票或是火車票;
第三,訂酒店;
第四,租車。
這四件事中,前三件必需完全成功,我們才能出行,而第四件事只是一個錦上添花的事,但第四件事一旦確定,那么也會成為整個事務的一部分。這些事都是要向不同的組織或系統請求。我們可以並行地做這些事,而如果某個事有變化,其它的事都會跟着出現一些變化。
設想下面的幾種情況。
- 我沒有訂到返程機票,那么我就去不了了。我需要把訂到的去程機票,酒店、租到的車都給取消了,並且把請的假也取消了。
- 如果我假也請好了,機票,酒店也訂好了,只是車沒租到,那么並不影響我出行這個事,整個事還是可以繼續的。
- 如果我的飛機因為天氣原因取消或是晚點了,那么我被迫要去調整和修改我的酒店預訂和租車的預訂。
從人類的實際生活當中,我們可以看出,上述的這些情況都是天天在發生的事情。所以,我們的分布式系統也是一樣的,也是需要處理這樣的事情——就是當條件不滿足,或是有變化的時候,需要從業務上做相應的整體事務的補償。
對於業務補償來說,首先需要將服務做成冪等性的,如果一個事務失敗了或是超時了,我們需要不斷地重試,努力地達到最終我們想要的狀態。然后,如果我們不能達到這個我們想要的狀態,我們需要把整個狀態恢復到之前的狀態。另外,如果有變化的請求,我們需要啟動整個事務的業務更新機制。
業務補償機制特點
由上可知,一個好的業務補償機制需要做到下面這幾點。
- 要能清楚地描述出要達到什么樣的狀態(比如:請假、機票、酒店這三個都必須成功,租車是可選的),以及如果其中的條件不滿足,那么,我們要回退到哪一個狀態。這就是所謂的整個業務的起始狀態定義。
- 當整條業務跑起來的時候,我們可以串行或並行地做這些事。對於旅游訂票是可以並行的,但是對於網購流程(下單、支付、送貨)是不能並行的。總之,我們的系統需要努力地通過一系列的操作達到一個我們想要的狀態。如果達不到,就需要通過補償機制回滾到之前的狀態。這就是所謂的狀態擬合。
- 對於已經完成的事務進行整體修改,可以考慮成一個修改事務。
其實,在純技術的世界里也有這樣的事。比如,線上運維系統需要發布一個新的服務或是對一個已有的服務進行水平擴展,我們需要先找到相應的機器,然后初始化環境,再部署上應用,再做相應的健康檢查,最后接入流量。這一系列的動作都要完全成功,所以,我們的部署系統就需要管理好整個過程和相關的運行狀態。
業務補償的設計重點
業務補償主要做兩件事。
- 努力地把一個業務流程執行完成。
- 如果執行不下去,需要啟動補償機制,回滾業務流程。
所以,下面是幾個重點。
- 因為要把一個業務流程執行完成,需要這個流程中所涉及的服務方支持冪等性。並且在上游有重試機制。
- 我們需要小心維護和監控整個過程的狀態,所以,千萬不要把這些狀態放到不同的組件中,最好是一個業務流程的控制方來做這個事,也就是一個工作流引擎。所以,這個工作流引擎是需要高可用和穩定的。這就好像旅行代理機構一樣,我們把需求告訴它,它會幫我們搞定所有的事。如果有問題,也會幫我們回滾和補償的。
- 補償的業務邏輯和流程不一定非得是嚴格反向操作。有時候可以並行,有時候,可能會更簡單。總之,設計業務正向流程的時候,也需要設計業務的反向補償流程。
- 我們要清楚地知道,業務補償的業務邏輯是強業務相關的,很難做成通用的。
- 下層的業務方最好提供短期的資源預留機制。就像電商中的把貨品的庫存預先占住等待用戶在 15 分鍾內支付。如果沒有收到用戶的支付,則釋放庫存。然后回滾到之前的下單操作,等待用戶重新下單。
站在巨人的肩膀上
- 陳皓 左耳聽風專欄