分布式事務(wù)解決方案以及 .Net Core 下的實現(xiàn)(上)

數(shù)據(jù)一致性是構(gòu)建業(yè)務(wù)系統(tǒng)需要考慮的重要問題 嘱朽, 以往我們是依靠數(shù)據(jù)庫來保證數(shù)據(jù)的一致性。但是在微服務(wù)架構(gòu)以及分布式環(huán)境下實現(xiàn)數(shù)據(jù)一致性是一個很有挑戰(zhàn)的的問題读处。最近在研究分布式事物,分布式的解決方案有很多解決方案,也讓我在研究的同時也引發(fā)了很多思考猎唁。今天我想講的是分布式事物解決方案是和saga有關(guān)。

原文地址:微服務(wù)場景下的數(shù)據(jù)一致性解決方案

incubator-servicecomb-saga地址:incubator-servicecomb-saga

servicecomb-saga-csharp(servicecomb-saga netcore sdk)地址:servicecomb-saga-csharp

根據(jù)原文做一些解釋性的地方 方便更加理解

單體應(yīng)用的數(shù)據(jù)一致性

我就給大家講一個國外經(jīng)常用到的例子吧顷蟆,就是假如有一家大型的企業(yè)诫隅,下屬有航空公司、租車公司帐偎、和連鎖酒店逐纬。這個大公司為客戶提供一站式的旅游行程規(guī)劃服務(wù),這樣客戶只需要提供出行目的地削樊, 這個大公司能幫助客戶預(yù)訂機票豁生、租車、以及預(yù)訂酒店漫贞。從業(yè)務(wù)的角度甸箱,我們必須保證上述三個服務(wù)的預(yù)訂都完成才能滿足一個成功的旅游行程,否則不能成行迅脐。

我們的單體應(yīng)用要滿足這個需求非常簡單芍殖,只需將這個三個服務(wù)請求放到同一個數(shù)據(jù)庫事務(wù)中,數(shù)據(jù)庫會幫我們保證全部成功或者全部回滾谴蔑。

image.png

這三個服務(wù)上線公司滿意豌骏,客戶也很滿意

微服務(wù)場景下的數(shù)據(jù)一致性

隨之時間的推移,這個大企業(yè)的行程規(guī)劃服務(wù)非常成功树碱,用戶量劇增上百倍肯适。企業(yè)的下屬航空公司、租車公司成榜、和連鎖酒店也相繼推出了更多服務(wù)以滿足客戶需求框舔, 我們的應(yīng)用和開發(fā)團隊也因此日漸龐大。如今我們的單體應(yīng)用已變得如此復(fù)雜赎婚,以至于沒人了解整個應(yīng)用是怎么運作的刘绣。更糟的是新功能的上線現(xiàn)在需要所有研發(fā)團隊合作, 日夜奮戰(zhàn)數(shù)周才能完成挣输∥撤铮看著市場占有率每況愈下,公司高層對研發(fā)部門越來越不滿意撩嚼。

經(jīng)過數(shù)輪討論停士,領(lǐng)導(dǎo)最終決定將龐大的單體應(yīng)用一分為四:機票預(yù)訂服務(wù)挖帘、租車服務(wù)、酒店預(yù)訂服務(wù)恋技、和支付服務(wù)拇舀。服務(wù)各自使用自己的數(shù)據(jù)庫,并通過HTTP協(xié)議通信蜻底。 負責(zé)各服務(wù)的團隊根據(jù)市場需求按照自己的開發(fā)節(jié)奏發(fā)版上線骄崩。如今我們面臨新的挑戰(zhàn):如何保證最初三個服務(wù)的預(yù)訂都完成才能滿足一個成功的旅游行程, 否則不能成行的業(yè)務(wù)規(guī)則薄辅?現(xiàn)在服務(wù)有各自的邊界要拂,而且數(shù)據(jù)庫選型也不盡相同,通過數(shù)據(jù)庫保證數(shù)據(jù)一致性的方案已不可行站楚。


image.png

Sagas

經(jīng)過一段時間的查找脱惰,我發(fā)現(xiàn)了一篇論文,1987年Hector & Kenneth 發(fā)表論文 Sagas論文地址

Saga是一個長活事務(wù)(Long Live Transaction (LLT))源请,可被分解成可以交錯運行的子事務(wù)集合枪芒。其中每個子事務(wù)都是一個保持數(shù)據(jù)庫一致性的真實事務(wù)(LLT = T1 + T2 + T3 + ... + Tn)。每個本地事務(wù)Tx 有對應(yīng)的補償 Cx谁尸。

在大企業(yè)的業(yè)務(wù)場景下舅踪,一個行程規(guī)劃的事務(wù)就是一個Saga,其中包含四個子事務(wù):機票預(yù)訂良蛮、租車抽碌、酒店預(yù)訂、和支付决瞳。

image.png

根據(jù)上面提到的公式

當每個saga子事務(wù) T1, T2, …, Tn 都有對應(yīng)的補償定義 C1, C2, …, Cn-1, 那么saga系統(tǒng)可以保證 [1]子事務(wù)序列 T1, T2, …, Tn得以完成 (最佳情況)或者序列
T1, T2, …, Tj, Cj, …,
C2, C1, 0 < j < n,
得以完成

image.png

換句話說货徙,通過上述定義的事務(wù)/補償,saga保證滿足以下業(yè)務(wù)規(guī)則:

所有的預(yù)訂都被執(zhí)行成功皮胡,如果任何一個失敗痴颊,都會被取消
如果最后一步付款失敗,所有預(yù)訂也將被取消屡贺,這些取消就是所謂的補償蠢棱。

Saga的恢復(fù)方式

原論文中描述了兩種類型的Saga恢復(fù)方式:

向后恢復(fù) 補償所有已完成的事務(wù),如果任一子事務(wù)失敗甩栈。向前恢復(fù) 重試失敗的事務(wù)泻仙,假設(shè)每個子事務(wù)最終都會成功

顯然,向前恢復(fù)沒有必要提供補償事務(wù)量没,如果你的業(yè)務(wù)中玉转,子事務(wù)(最終)總會成功,或補償事務(wù)難以定義或不可能殴蹄,向前恢復(fù)更符合你的需求究抓。

理論上補償事務(wù)永不失敗猾担,然而,在分布式世界中漩蟆,我們來想想極端的情況垒探,無非就是往三種可能去考慮,成功怠李,失敗,超時(有可能成功蛤克,也有可能失敗)捺癞。那么服務(wù)器可能會宕機,網(wǎng)絡(luò)可能會失敗构挤,甚至數(shù)據(jù)中心也可能會停電髓介。在這種情況下我們能做些什么? 最后的手段是提供回退措施筋现,比如人工干預(yù)唐础。

補充說明:ACID與SAGA

  • 原子性(Atomicity):Saga只提供ACD保證,原子性(通過Saga協(xié)調(diào)器實現(xiàn))
  • 一致性(Consistency):本地事務(wù) + Saga log
  • 隔離性(Isolation):Saga不保證
  • 持久性(Durability):Saga log 提供
有很多朋友會說怎么不提供隔離性啊矾飞?

例子地址:地址

  • 兩個Saga事務(wù)同時操作一個資源會出現(xiàn)數(shù)據(jù)語義不一致的的情況
  • 兩個Saga事務(wù)同時操作一個訂單 一膨,彼此操作會覆蓋對方(更新丟失)
  • 兩個Saga事務(wù)同時訪問扣款賬號槐沼,無法看到退款 (臟讀取問題)
  • 在一個Saga事務(wù)內(nèi)优构,數(shù)據(jù)被其他事務(wù)修改前后的讀取值不一致(模糊讀取問題)
面對以上問題我們應(yīng)該如何應(yīng)對隔離性問題呢?

下面給出對應(yīng)的解決方案

  • 隔離的本質(zhì)是控制并發(fā)镇防,防止并發(fā)事務(wù)操作相同資源而引起結(jié)果錯亂
  • 在應(yīng)用層面加入邏輯鎖的邏輯申眼。
  • 業(yè)務(wù)層面采用預(yù)先凍結(jié)資金的方式隔離此部分資金瞒津。
  • 業(yè)務(wù)操作過程中通過及時讀取當前狀態(tài)的方式獲取更新。

使用Saga的條件

Saga看起來很有希望滿足我們的需求括尸。所有長活事務(wù)都可以這樣做嗎巷蚪?這里有一些限制:

Saga只允許兩個層次的嵌套,頂級的Saga和簡單子事務(wù) [1]
在外層濒翻,全原子性不能得到滿足屁柏。也就是說,sagas可能會看到其他sagas的部分結(jié)果 [1]
每個子事務(wù)應(yīng)該是獨立的原子行為 [2]
在我們的業(yè)務(wù)場景下肴焊,航班預(yù)訂前联、租車、酒店預(yù)訂和付款是自然獨立的行為娶眷,而且每個事務(wù)都可以用對應(yīng)服務(wù)的數(shù)據(jù)庫保證原子操作似嗤。
我們在行程規(guī)劃事務(wù)層面也不需要原子性。一個用戶可以預(yù)訂最后一張機票届宠,而后由于信用卡余額不足而被取消烁落。同時另一個用戶可能開始會看到已無余票乘粒, 接著由于前者預(yù)訂被取消,最后一張機票被釋放伤塌,而搶到最后一個座位并完成行程規(guī)劃灯萍。

補償也有需考慮的事項:

補償事務(wù)從語義角度撤消了事務(wù)Ti的行為,但未必能將數(shù)據(jù)庫返回到執(zhí)行Ti時的狀態(tài)每聪。(例如旦棉,如果事務(wù)觸發(fā)導(dǎo)彈發(fā)射, 則可能無法撤消此操作)
但這對我們的業(yè)務(wù)來說不是問題药薯。其實難以撤消的行為也有可能被補償绑洛。例如,發(fā)送電郵的事務(wù)可以通過發(fā)送解釋問題的另一封電郵來補償童本。

現(xiàn)在我們有了通過Saga來解決數(shù)據(jù)一致性問題的方案真屯。它允許我們成功地執(zhí)行所有事務(wù),或在任何事務(wù)失敗的情況下穷娱,補償已成功的事務(wù)绑蔫。 雖然Saga不提供ACID保證,但仍適用于許多數(shù)據(jù)最終一致性的場景泵额。那我們?nèi)绾卧O(shè)計一個Saga系統(tǒng)配深?

Saga Log

Saga保證所有的子事務(wù)都得以完成或補償,但Saga系統(tǒng)本身也可能會崩潰梯刚。Saga崩潰時可能處于以下幾個狀態(tài):

  • Saga收到事務(wù)請求凉馆,但尚未開始。因子事務(wù)對應(yīng)的微服務(wù)狀態(tài)未被Saga修改亡资,我們什么也不需要做澜共。
  • 一些子事務(wù)已經(jīng)完成。重啟后锥腻,Saga必須接著上次完成的事務(wù)恢復(fù)嗦董。
  • 子事務(wù)已開始,但尚未完成瘦黑。由于遠程服務(wù)可能已完成事務(wù)京革,也可能事務(wù)失敗,甚至服務(wù)請求超時幸斥,saga只能重新發(fā)起之前未確認完成的子事務(wù)匹摇。這意味著子事務(wù)必須冪等。
  • 子事務(wù)失敗甲葬,其補償事務(wù)尚未開始廊勃。Saga必須在重啟后執(zhí)行對應(yīng)補償事務(wù)。

補償事務(wù)已開始但尚未完成经窖。解決方案與上一個相同坡垫。這意味著補償事務(wù)也必須是冪等的梭灿。
所有子事務(wù)或補償事務(wù)均已完成,與第一種情況相同冰悠。
為了恢復(fù)到上述狀態(tài)堡妒,我們必須追蹤子事務(wù)及補償事務(wù)的每一步。我們決定通過事件的方式達到以上要求溉卓,并將以下事件保存在名為saga log的持久存儲中:

  • Saga started event 保存整個saga請求皮迟,其中包括多個事務(wù)/補償請求
  • Transaction started event 保存對應(yīng)事務(wù)請求
  • Transaction ended event 保存對應(yīng)事務(wù)請求及其回復(fù)
  • Transaction aborted event 保存對應(yīng)事務(wù)請求和失敗的原因
  • Transaction compensated event 保存對應(yīng)補償請求及其回復(fù)
  • Saga ended event 標志著saga事務(wù)請求的結(jié)束,不需要保存任何內(nèi)容
image.png

通過將這些事件持久化在saga log中的诵,我們可以將saga恢復(fù)到上述任何狀態(tài)万栅。

由于Saga只需要做事件的持久化,而事件內(nèi)容以JSON的形式存儲西疤,Saga log的實現(xiàn)非常靈活,數(shù)據(jù)庫(SQL或NoSQL)休溶,持久消息隊列代赁,甚至普通文件可以用作事件存儲, 當然有些能更快得幫saga恢復(fù)狀態(tài)兽掰。

Saga請求的數(shù)據(jù)結(jié)構(gòu)

在我們的業(yè)務(wù)場景下芭碍,航班預(yù)訂、租車孽尽、和酒店預(yù)訂沒有依賴關(guān)系窖壕,可以并行處理,但對于我們的客戶來說杉女,只在所有預(yù)訂成功后一次付費更加友好瞻讽。 那么這四個服務(wù)的事務(wù)關(guān)系可以用下圖表示:

image.png

將行程規(guī)劃請求的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)為有向非循環(huán)圖恰好合適。 圖的根是saga啟動任務(wù)熏挎,葉是saga結(jié)束任務(wù)速勇。

image.png

Parallel Saga

如上所述,航班預(yù)訂坎拐,租車和酒店預(yù)訂可以并行處理烦磁。但是這樣做會造成另一個問題:如果航班預(yù)訂失敗,而租車正在處理怎么辦哼勇?我們不能一直等待租車服務(wù)回應(yīng)都伪, 因為不知道需要等多久。

最好的辦法是再次發(fā)送租車請求积担,獲得回應(yīng)陨晶,以便我們能夠繼續(xù)補償操作。但如果租車服務(wù)永不回應(yīng)磅轻,我們可能需要采取回退措施珍逸,比如手動干預(yù)逐虚。

超時的預(yù)訂請求可能最后仍被租車服務(wù)收到,這時服務(wù)已經(jīng)處理了相同的預(yù)訂和取消請求谆膳。

image.png

因此叭爱,服務(wù)的實現(xiàn)必須保證補償請求執(zhí)行以后,再次收到的對應(yīng)事務(wù)請求無效漱病。 Caitie McCaffrey在她的演講Distributed Sagas: A Protocol for Coordinating Microservices中把這個稱為可交換的補償請求 (commutative compensating request)买雾。

分布式saga架構(gòu)

分布式的Saga借鑒了zipkin的思想,Omega就是類似探針的形式杨帽,上報saga事件漓穿,然后Alpha是屬于Saga的ProcessManager.也就是協(xié)調(diào)器的東西。

  • alpha充當協(xié)調(diào)者的角色注盈,主要負責(zé)對事務(wù)的事件進行持久化存儲以及協(xié)調(diào)子事務(wù)的狀態(tài)晃危,使其得以最終與全局事務(wù)的狀態(tài)保持一致。
  • omega是微服務(wù)中內(nèi)嵌的一個agent老客,負責(zé)對網(wǎng)絡(luò)請求進行攔截并向alpha上報事務(wù)事件僚饭,并在異常情況下根據(jù)alpha下發(fā)的指令執(zhí)行相應(yīng)的補償操作。
image.png

接下來我們看下Omega的內(nèi)部實現(xiàn)

omega是微服務(wù)中內(nèi)嵌的一個agent胧砰。當服務(wù)收到請求時鳍鸵,omega會將其攔截并從中提取請求信息中的全局事務(wù)id作為其自身的全局事務(wù)id(即Saga事件id),并提取本地事務(wù)id作為其父事務(wù)id尉间。在預(yù)處理階段偿乖,alpha會記錄事務(wù)開始的事件;在后處理階段哲嘲,alpha會記錄事務(wù)結(jié)束的事件贪薪。因此,每個成功的子事務(wù)都有一一對應(yīng)的開始及結(jié)束事件撤蚊。

image.png

我們再看下 他們是如何通信的

服務(wù)間通信的流程與Zipkin的類似古掏。在服務(wù)生產(chǎn)方,omega會攔截請求中事務(wù)相關(guān)的id來提取事務(wù)的上下文侦啸。在服務(wù)消費方槽唾,omega會在請求中注入事務(wù)相關(guān)的id來傳遞事務(wù)的上下文。通過服務(wù)提供方和服務(wù)消費方的這種協(xié)作處理光涂,子事務(wù)能連接起來形成一個完整的全局事務(wù)庞萍。

image.png

借助zipkin的思想就可以讓整一個事務(wù)組形成一個鏈式結(jié)構(gòu)。

image.png

Saga 具體處理流程

Saga處理場景是要求相關(guān)的子事務(wù)提供事務(wù)處理函數(shù)同時也提供補償函數(shù)忘闻。Saga協(xié)調(diào)器alpha會根據(jù)事務(wù)的執(zhí)行情況向omega發(fā)送相關(guān)的指令钝计,確定是否向前重試或者向后恢復(fù)。

成功場景
成功場景下,每個事務(wù)都會有開始和有對應(yīng)的結(jié)束事件私恬。


image.png

異常場景
異常場景下债沮,omega會向alpha上報中斷事件,然后alpha會向該全局事務(wù)的其它已完成的子事務(wù)發(fā)送補償指令本鸣,確保最終所有的子事務(wù)要么都成功疫衩,要么都回滾。


image.png

超時場景 (需要調(diào)整)
超時場景下荣德,已超時的事件會被alpha的定期掃描器檢測出來闷煤,與此同時,該超時事務(wù)對應(yīng)的全局事務(wù)也會被中斷涮瞻。


image.png

以上都是介紹完incubator-servicecomb-saga 總體架構(gòu)鲤拿。我覺得它的idea很nice,所以我和水哥,還有老杜做了一個很有趣的事情署咽。什么事情呢近顷?就是實現(xiàn)了Omega這個客戶端,github地址在這里:servicecomb-saga-csharp,目前實現(xiàn)上面的三種場景宁否。

下篇結(jié)合實際的sample和大家講解下netcore下的實現(xiàn)幕庐,這篇文章讓大家整體的了解什么是saga。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末家淤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瑟由,更是在濱河造成了極大的恐慌絮重,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歹苦,死亡現(xiàn)場離奇詭異青伤,居然都是意外死亡,警方通過查閱死者的電腦和手機殴瘦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門狠角,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚪腋,你說我怎么就攤上這事丰歌。” “怎么了屉凯?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵立帖,是天一觀的道長。 經(jīng)常有香客問我悠砚,道長晓勇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮绑咱,結(jié)果婚禮上绰筛,老公的妹妹穿的比我還像新娘。我一直安慰自己描融,他們只是感情好铝噩,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稼稿,像睡著了一般薄榛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上让歼,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天敞恋,我揣著相機與錄音,去河邊找鬼谋右。 笑死硬猫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的改执。 我是一名探鬼主播啸蜜,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辈挂!你這毒婦竟也來了衬横?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤终蒂,失蹤者是張志新(化名)和其女友劉穎蜂林,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拇泣,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡噪叙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了霉翔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睁蕾。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖债朵,靈堂內(nèi)的尸體忽然破棺而出子眶,到底是詐尸還是另有隱情,我是刑警寧澤葱弟,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布壹店,位于F島的核電站,受9級特大地震影響芝加,放射性物質(zhì)發(fā)生泄漏硅卢。R本人自食惡果不足惜射窒,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望将塑。 院中可真熱鬧脉顿,春花似錦、人聲如沸点寥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敢辩。三九已至蔽莱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戚长,已是汗流浹背盗冷。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留同廉,地道東北人仪糖。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像迫肖,于是被迫代替她去往敵國和親锅劝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 概述 servicecomb-saga是一個微服務(wù)應(yīng)用的數(shù)據(jù)最終一致性解決方案蟆湖,是一種基于在2PC和TCC兩者之間...
    fantuanjiaozi閱讀 7,582評論 0 14
  • 各位家人下午好: 周一學(xué)習(xí)了《干法》中的改變命運故爵,小的時候,聽老人們說隅津,人的命天注定稠集,不錯,命運這東西饥瓷,在我們的人...
    3411022510a9閱讀 106評論 0 0
  • [特別標注,文章首發(fā)在CSDN痹籍,也是本人原創(chuàng)呢铆,下面放一下地址,主頁地址] 0.閱讀本文前你需要手里掌握一個KVOD...
    帥裂蒼穹的七爺爺閱讀 595評論 0 1
  • 晴蹲缠。 早晨起的不晚棺克,但是走的比較晚。憲法线定,聽的依舊不仔細娜谊。吃了全家的三明治。中午又點了外賣斤讥,又吃了米線纱皆。當你沉睡時...
    Cheryl_ak717閱讀 256評論 0 0
  • 上個禮拜基本上又是和身體在對抗中度過了,所以也沒有做成什么事情,快到三十歲了派草,身體卻越發(fā)的比不上少年了搀缠,十幾年沒有...
    陳總閱讀 296評論 0 1