分布式事務(wù)解決的方案下
上期我們介紹分布式事務(wù)解決方案中的二階段提交和三階段提交。
今天將繼續(xù)上期內(nèi)容介紹后面三種解決方案。
一、什么是補(bǔ)償事務(wù)?
TCC (Try Confifirm Cancel)是服務(wù)化的二階段編程模型,采用的補(bǔ)償機(jī)制:

TCC 其實(shí)就是采用的補(bǔ)償機(jī)制,其核心思想是:針對(duì)每個(gè)操作,都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷)操作。
它分為三個(gè)步驟:
Try 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做檢測(cè)及資源預(yù)留。
Confifirm 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做確認(rèn)提交,Try階段執(zhí)行成功并開始執(zhí)行 Confifirm階段時(shí),默認(rèn) Confifirm階段是不會(huì)出錯(cuò)的。即:只要Try成功,Confifirm一定成功。
Cancel 階段主要是在業(yè)務(wù)執(zhí)行錯(cuò)誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消,預(yù)留資源釋放。
舉個(gè)例子,假入你要向 隔壁老王轉(zhuǎn)賬,思路大概是:我們有一個(gè)本地方法,里面依次調(diào)用步驟:?1、首先在?Try?階段,要先調(diào)用遠(yuǎn)程接口把 你 和 老王 的錢給凍結(jié)起來。?2、在?Confifirm?階段,執(zhí)行遠(yuǎn)程調(diào)用的轉(zhuǎn)賬的操作,轉(zhuǎn)賬成功進(jìn)行解凍。?3、如果第2步執(zhí)行成功,那么轉(zhuǎn)賬成功,如果第二步執(zhí)行失敗,則調(diào)用遠(yuǎn)程凍結(jié)接口對(duì)應(yīng)的解凍方法?(Cancel)。
優(yōu)點(diǎn):
性能提升:具體業(yè)務(wù)來實(shí)現(xiàn)控制資源鎖的粒度變小,不會(huì)鎖定整個(gè)資源。
數(shù)據(jù)最終一致性:基于 Confifirm 和 Cancel 的冪等性,保證事務(wù)最終完成確認(rèn)或者取消,保證數(shù)據(jù)的一致性。
可靠性:解決了 XA 協(xié)議的協(xié)調(diào)者單點(diǎn)故障問題,由主業(yè)務(wù)方發(fā)起并控制整個(gè)業(yè)務(wù)活動(dòng),業(yè)務(wù)活動(dòng)管理器也變成多點(diǎn),引入集群。
缺點(diǎn):
TCC 的 Try、Confifirm 和 Cancel 操作功能要按具體業(yè)務(wù)來實(shí)現(xiàn),業(yè)務(wù)耦合度較高,提高了開發(fā)成本
二、消息隊(duì)列是怎么實(shí)現(xiàn)的?
本地消息表(異步確保)????
本地消息表這種實(shí)現(xiàn)方式應(yīng)該是業(yè)界使用最多的,其核心思想是將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理,這種思路是來源于ebay。我們可以從下面的流程圖中看出其中的一些細(xì)節(jié):

基本思路就是:
消息生產(chǎn)方,需要額外建一個(gè)消息表,并記錄消息發(fā)送狀態(tài)。消息表和業(yè)務(wù)數(shù)據(jù)要在一個(gè)事務(wù)里提交,也就是說他們要在一個(gè)數(shù)據(jù)庫(kù)里面。然后消息會(huì)經(jīng)過MQ發(fā)送到消息的消費(fèi)方。如果消息發(fā)送失敗,會(huì)進(jìn)行重試發(fā)送。
消息消費(fèi)方,需要處理這個(gè)消息,并完成自己的業(yè)務(wù)邏輯。此時(shí)如果本地事務(wù)處理成功,表明已經(jīng)處理成功了,如果處理失敗,那么就會(huì)重試執(zhí)行。如果是業(yè)務(wù)上面的失敗,可以給生產(chǎn)方發(fā)送一個(gè)業(yè)務(wù)補(bǔ)償消息,通知生產(chǎn)方進(jìn)行回滾等操作。
生產(chǎn)方和消費(fèi)方定時(shí)掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍。如果有靠譜的自動(dòng)對(duì)賬補(bǔ)賬邏輯,這種方案還是非常實(shí)用的。
這種方案遵循BASE理論,采用的是最終一致性,筆者認(rèn)為是這幾種方案里面比較適合實(shí)際業(yè)務(wù)場(chǎng)景的,即不會(huì)出現(xiàn)像2PC那樣復(fù)雜的實(shí)現(xiàn)(當(dāng)調(diào)用鏈很長(zhǎng)的時(shí)候,2PC的可用性是非常低的),也不會(huì)像TCC那樣可能出現(xiàn)確認(rèn)或者回滾不了的情況。
優(yōu)點(diǎn):
一種非常經(jīng)典的實(shí)現(xiàn),避免了分布式事務(wù),實(shí)現(xiàn)了最終一致性。在 .NET中 有現(xiàn)成的解決方案。
缺點(diǎn):
消息表會(huì)耦合到業(yè)務(wù)系統(tǒng)中,如果沒有封裝好的解決方案,會(huì)有很多雜活需要處理。
MQ 事務(wù)消息
有一些第三方的MQ是支持事務(wù)消息的,比如RocketMQ,他們支持事務(wù)消息的方式也是類似于采用的二階段提交,但是市面上一些主流的MQ都是不支持事務(wù)消息的,比如 RabbitMQ 和 Kafka 都不支持。


遺憾的是,RocketMQ并沒有 .NET 客戶端。
優(yōu)點(diǎn):
實(shí)現(xiàn)了最終一致性,不需要依賴本地?cái)?shù)據(jù)庫(kù)事務(wù)。
缺點(diǎn):
實(shí)現(xiàn)難度大,主流MQ不支持,沒有.NET客戶端,RocketMQ事務(wù)消息部分代碼也未開源。
三、說說Sagas事務(wù)模型
Saga模式是一種分布式異步事務(wù),一種最終一致性事務(wù),是一種柔性事務(wù),有兩種不同的方式來實(shí)現(xiàn)saga事務(wù),最流行的兩種方式是:
一、 事件/編排Choreography:沒有中央?yún)f(xié)調(diào)器(沒有單點(diǎn)風(fēng)險(xiǎn))時(shí),每個(gè)服務(wù)產(chǎn)生并聆聽其他服務(wù)的事件,并決定是否應(yīng)采取行動(dòng)。
該實(shí)現(xiàn)第一個(gè)服務(wù)執(zhí)行一個(gè)事務(wù),然后發(fā)布一個(gè)事件。該事件被一個(gè)或多個(gè)服務(wù)進(jìn)行監(jiān)聽,這些服務(wù)再執(zhí)行本地事務(wù)并發(fā)布(或不發(fā)布)新的事件,當(dāng)最后一個(gè)服務(wù)執(zhí)行本地事務(wù)并且不發(fā)布任何事件時(shí),意味著分布式事務(wù)結(jié)束,或者它發(fā)布的事件沒有被任何Saga參與者聽到都意味著事務(wù)結(jié)束。

處理流程說明:

訂購(gòu)服務(wù)和支付服務(wù)會(huì)監(jiān)聽到上面庫(kù)存服務(wù)的這一事件:
支付服務(wù)會(huì)退款給客戶。
訂單服務(wù)將訂單狀態(tài)設(shè)置為失敗。
優(yōu)點(diǎn):事件/編排是實(shí)現(xiàn)Saga模式的自然方式; 它很簡(jiǎn)單,容易理解,不需要太多的努力來構(gòu)建,所有參與者都是松散耦合的,因?yàn)樗麄儽舜酥g沒有直接的耦合。如果您的事務(wù)涉及2至4個(gè)步驟,則可能是非常合適的。
二、 命令/協(xié)調(diào)orchestrator:中央?yún)f(xié)調(diào)器負(fù)責(zé)集中處理事件的決策和業(yè)務(wù)邏輯排序。
saga協(xié)調(diào)器orchestrator以命令/回復(fù)的方式與每項(xiàng)服務(wù)進(jìn)行通信,告訴他們應(yīng)該執(zhí)行哪些操作。


優(yōu)點(diǎn):
避免服務(wù)之間的循環(huán)依賴關(guān)系,因?yàn)閟aga協(xié)調(diào)器會(huì)調(diào)用saga參與者,但參與者不會(huì)調(diào)用協(xié)調(diào)器。
集中分布式事務(wù)的編排。
只需要執(zhí)行命令/回復(fù)(其實(shí)回復(fù)消息也是一種事件消息),降低參與者的復(fù)雜性。
在添加新步驟時(shí),事務(wù)復(fù)雜性保持線性,回滾更容易管理。
如果在第一筆交易還沒有執(zhí)行完,想改變有第二筆事務(wù)的目標(biāo)對(duì)象,則可以輕松地將其暫停在協(xié)調(diào)器上,直到第一筆交易結(jié)束。