1 Saga相關(guān)概念
1987年普林斯頓大學(xué)的Hector Garcia-Molina和Kenneth Salem發(fā)表了一篇Paper Sagas愈案,講述的是如何處理long lived transaction(長活事務(wù))。Saga是一個長活事務(wù)可被分解成可以交錯運(yùn)行的子事務(wù)集合顷啼。其中每個子事務(wù)都是一個保持?jǐn)?shù)據(jù)庫一致性的真實(shí)事務(wù)。
論文地址:sagas
1.1 Saga的組成
- 每個Saga由一系列sub-transaction Ti 組成
- 每個Ti 都有對應(yīng)的補(bǔ)償動作Ci沐扳,補(bǔ)償動作用于撤銷Ti造成的結(jié)果
可以看到,和TCC相比,Saga沒有“預(yù)留”動作袋毙,它的Ti就是直接提交到庫。
Saga的執(zhí)行順序有兩種:
- T1, T2, T3, ..., Tn
- T1, T2, ..., Tj, Cj,..., C2, C1冗尤,其中0 < j < n
Saga定義了兩種恢復(fù)策略:
- backward recovery听盖,向后恢復(fù),補(bǔ)償所有已完成的事務(wù)裂七,如果任一子事務(wù)失敗皆看。即上面提到的第二種執(zhí)行順序,其中j是發(fā)生錯誤的sub-transaction背零,這種做法的效果是撤銷掉之前所有成功的sub-transation腰吟,使得整個Saga的執(zhí)行結(jié)果撤銷。
- forward recovery徙瓶,向前恢復(fù)毛雇,重試失敗的事務(wù),假設(shè)每個子事務(wù)最終都會成功侦镇。適用于必須要成功的場景灵疮,執(zhí)行順序是類似于這樣的:T1, T2, ..., Tj(失敗), Tj(重試),..., Tn,其中j是發(fā)生錯誤的sub-transaction壳繁。該情況下不需要Ci震捣。
顯然,向前恢復(fù)沒有必要提供補(bǔ)償事務(wù)闹炉,如果你的業(yè)務(wù)中蒿赢,子事務(wù)(最終)總會成功,或補(bǔ)償事務(wù)難以定義或不可能渣触,向前恢復(fù)更符合你的需求诉植。
理論上補(bǔ)償事務(wù)永不失敗,然而昵观,在分布式世界中晾腔,服務(wù)器可能會宕機(jī),網(wǎng)絡(luò)可能會失敗啊犬,甚至數(shù)據(jù)中心也可能會停電灼擂。在這種情況下我們能做些什么? 最后的手段是提供回退措施觉至,比如人工干預(yù)剔应。
1.2 Saga的使用條件
Saga看起來很有希望滿足我們的需求。所有長活事務(wù)都可以這樣做嗎?這里有一些限制:
- Saga只允許兩個層次的嵌套峻贮,頂級的Saga和簡單子事務(wù)
- 在外層席怪,全原子性不能得到滿足。也就是說纤控,sagas可能會看到其他sagas的部分結(jié)果
- 每個子事務(wù)應(yīng)該是獨(dú)立的原子行為
- 在我們的業(yè)務(wù)場景下挂捻,各個業(yè)務(wù)環(huán)境(如:航班預(yù)訂、租車船万、酒店預(yù)訂和付款)是自然獨(dú)立的行為刻撒,而且每個事務(wù)都可以用對應(yīng)服務(wù)的數(shù)據(jù)庫保證原子操作。
補(bǔ)償也有需考慮的事項(xiàng):
- 補(bǔ)償事務(wù)從語義角度撤消了事務(wù)Ti的行為耿导,但未必能將數(shù)據(jù)庫返回到執(zhí)行Ti時的狀態(tài)声怔。(例如,如果事務(wù)觸發(fā)導(dǎo)彈發(fā)射舱呻, 則可能無法撤消此操作)
但這對我們的業(yè)務(wù)來說不是問題醋火。其實(shí)難以撤消的行為也有可能被補(bǔ)償。例如箱吕,發(fā)送電郵的事務(wù)可以通過發(fā)送解釋問題的另一封電郵來補(bǔ)償芥驳。
對于ACID的保證:
Saga對于ACID的保證和TCC一樣:
- 原子性(Atomicity):正常情況下保證。
- 一致性(Consistency)殖氏,在某個時間點(diǎn)晚树,會出現(xiàn)A庫和B庫的數(shù)據(jù)違反一致性要求的情況姻采,但是最終是一致的雅采。
- 隔離性(Isolation),在某個時間點(diǎn)慨亲,A事務(wù)能夠讀到B事務(wù)部分提交的結(jié)果婚瓜。
- 持久性(Durability),和本地事務(wù)一樣刑棵,只要commit則數(shù)據(jù)被持久巴刻。
Saga不提供ACID保證,因?yàn)樵有院透綦x性不能得到滿足蛉签。原論文描述如下:
full atomicity is not provided. That is, sagas may view the partial results of other sagas
通過saga log胡陪,saga可以保證一致性和持久性。
和TCC對比
Saga相比TCC的缺點(diǎn)是缺少預(yù)留動作碍舍,導(dǎo)致補(bǔ)償動作的實(shí)現(xiàn)比較麻煩:Ti就是commit柠座,比如一個業(yè)務(wù)是發(fā)送郵件,在TCC模式下片橡,先保存草稿(Try)再發(fā)送(Confirm)妈经,撤銷的話直接刪除草稿(Cancel)就行了。而Saga則就直接發(fā)送郵件了(Ti),如果要撤銷則得再發(fā)送一份郵件說明撤銷(Ci)吹泡,實(shí)現(xiàn)起來有一些麻煩骤星。
如果把上面的發(fā)郵件的例子換成:A服務(wù)在完成Ti后立即發(fā)送Event到ESB(企業(yè)服務(wù)總線,可以認(rèn)為是一個消息中間件)爆哑,下游服務(wù)監(jiān)聽到這個Event做自己的一些工作然后再發(fā)送Event到ESB洞难,如果A服務(wù)執(zhí)行補(bǔ)償動作Ci,那么整個補(bǔ)償動作的層級就很深泪漂。
不過沒有預(yù)留動作也可以認(rèn)為是優(yōu)點(diǎn):
- 有些業(yè)務(wù)很簡單廊营,套用TCC需要修改原來的業(yè)務(wù)邏輯,而Saga只需要添加一個補(bǔ)償動作就行了萝勤。
- TCC最少通信次數(shù)為2n露筒,而Saga為n(n=sub-transaction的數(shù)量)。
- 有些第三方服務(wù)沒有Try接口敌卓,TCC模式實(shí)現(xiàn)起來就比較tricky了慎式,而Saga則很簡單。
- 沒有預(yù)留動作就意味著不必?fù)?dān)心資源釋放的問題趟径,異常處理起來也更簡單(請對比Saga的恢復(fù)策略和TCC的異常處理)瘪吏。
2 Saga相關(guān)實(shí)現(xiàn)
Saga Log
Saga保證所有的子事務(wù)都得以完成或補(bǔ)償,但Saga系統(tǒng)本身也可能會崩潰蜗巧。Saga崩潰時可能處于以下幾個狀態(tài):
- Saga收到事務(wù)請求掌眠,但尚未開始。因子事務(wù)對應(yīng)的微服務(wù)狀態(tài)未被Saga修改幕屹,我們什么也不需要做蓝丙。
- 一些子事務(wù)已經(jīng)完成。重啟后望拖,Saga必須接著上次完成的事務(wù)恢復(fù)渺尘。
- 子事務(wù)已開始,但尚未完成说敏。由于遠(yuǎn)程服務(wù)可能已完成事務(wù)鸥跟,也可能事務(wù)失敗,甚至服務(wù)請求超時盔沫,saga只能重新發(fā)起之前未確認(rèn)完成的子事務(wù)医咨。這意味著子事務(wù)必須冪等。
- 子事務(wù)失敗架诞,其補(bǔ)償事務(wù)尚未開始拟淮。Saga必須在重啟后執(zhí)行對應(yīng)補(bǔ)償事務(wù)。
- 補(bǔ)償事務(wù)已開始但尚未完成侈贷。解決方案與上一個相同惩歉。這意味著補(bǔ)償事務(wù)也必須是冪等的等脂。
- 所有子事務(wù)或補(bǔ)償事務(wù)均已完成,與第一種情況相同撑蚌。
為了恢復(fù)到上述狀態(tài)上遥,我們必須追蹤子事務(wù)及補(bǔ)償事務(wù)的每一步。我們決定通過事件的方式達(dá)到以上要求争涌,并將以下事件保存在名為saga log的持久存儲中:
- Saga started event 保存整個saga請求粉楚,其中包括多個事務(wù)/補(bǔ)償請求
- Transaction started event 保存對應(yīng)事務(wù)請求
- Transaction ended event 保存對應(yīng)事務(wù)請求及其回復(fù)
- Transaction aborted event 保存對應(yīng)事務(wù)請求和失敗的原因
- Transaction compensated event 保存對應(yīng)補(bǔ)償請求及其回復(fù)
- Saga ended event 標(biāo)志著saga事務(wù)請求的結(jié)束,不需要保存任何內(nèi)容
通過將這些事件持久化在saga log中亮垫,我們可以將saga恢復(fù)到上述任何狀態(tài)模软。
由于Saga只需要做事件的持久化,而事件內(nèi)容以JSON的形式存儲饮潦,Saga log的實(shí)現(xiàn)非常靈活燃异,數(shù)據(jù)庫(SQL或NoSQL),持久消息隊(duì)列继蜡,甚至普通文件可以用作事件存儲回俐, 當(dāng)然有些能更快得幫saga恢復(fù)狀態(tài)。
注意事項(xiàng)
對于服務(wù)來說稀并,實(shí)現(xiàn)Saga有以下這些要求:
- Ti和Ci是冪等的仅颇。
- Ci必須是能夠成功的,如果無法成功則需要人工介入碘举。
- Ti - Ci和Ci - Ti的執(zhí)行結(jié)果必須是一樣的:sub-transaction被撤銷了忘瓦。
第一點(diǎn)要求Ti和Ci是冪等的,舉個例子引颈,假設(shè)在執(zhí)行Ti的時候超時了耕皮,此時我們是不知道執(zhí)行結(jié)果的,如果采用forward recovery策略就會再次發(fā)送Ti线欲,那么就有可能出現(xiàn)Ti被執(zhí)行了兩次甚疟,所以要求Ti冪等参萄。如果采用backward recovery策略就會發(fā)送Ci,而如果Ci也超時了苗桂,就會嘗試再次發(fā)送Ci逼泣,那么就有可能出現(xiàn)Ci被執(zhí)行兩次趴泌,所以要求Ci冪等。
第二點(diǎn)要求Ci必須能夠成功拉庶,這個很好理解嗜憔,因?yàn)椋绻鸆i不能執(zhí)行成功就意味著整個Saga無法完全撤銷氏仗,這個是不允許的吉捶。但總會出現(xiàn)一些特殊情況比如Ci的代碼有bug、服務(wù)長時間崩潰等,這個時候就需要人工介入了呐舔。
第三點(diǎn)乍看起來比較奇怪币励,舉例說明,還是考慮Ti執(zhí)行超時的場景珊拼,我們采用了backward recovery食呻,發(fā)送一個Ci,那么就會有三種情況:
- Ti的請求丟失了澎现,服務(wù)之前沒有仅胞、之后也不會執(zhí)行Ti
- Ti在Ci之前執(zhí)行
- Ci在Ti之前執(zhí)行
對于第1種情況,容易處理剑辫。對于第2干旧、3種情況,則要求Ti和Ci是可交換的(commutative)妹蔽,并且其最終結(jié)果都是sub-transaction被撤銷莱革。
3 Saga協(xié)調(diào)
協(xié)調(diào)saga:saga的實(shí)現(xiàn)包含協(xié)調(diào)saga步驟的邏輯。當(dāng)系統(tǒng)命令啟動saga時讹开,協(xié)調(diào)邏輯必須選擇并告知第一個saga參與者執(zhí)行本地事務(wù)盅视。一旦該事務(wù)完成,saga的排序協(xié)調(diào)選擇并調(diào)用下一個saga參與者旦万。這個過程一直持續(xù)到saga執(zhí)行了所有步驟闹击。如果任何本地事務(wù)失敗,則saga必須以相反的順序執(zhí)行補(bǔ)償事務(wù)成艘。構(gòu)建一個saga的協(xié)調(diào)邏輯有幾種不同的方法:
- 編排(Choreography):在saga參與者中分配決策和排序赏半。他們主要通過交換事件進(jìn)行溝通。
- 控制(Orchestration):在saga控制類中集中saga的協(xié)調(diào)邏輯淆两。一個saga控制者向saga參與者發(fā)送命令消息断箫,告訴他們要執(zhí)行哪些操作。
3.1 編排(Choreography)
基于編排的saga:實(shí)現(xiàn)sagas的一種方法是使用編排秋冰。當(dāng)使用編排時仲义,沒有中央?yún)f(xié)調(diào)員告訴saga參與者該做什么。相反剑勾,sagas參與者訂閱彼此的事件并做出相應(yīng)的響應(yīng)埃撵。
通過這個sagas的路徑如下:
- Order Service在APPROVAL_PENDING狀態(tài)下創(chuàng)建一個Order并發(fā)布OrderCreated事件。
- Consumer Service消費(fèi)OrderCreated事件虽另,驗(yàn)證消費(fèi)者是否可以下訂單暂刘,并發(fā)布ConsumerVerified事件。
- Kitchen Service消費(fèi)OrderCreated事件捂刺,驗(yàn)證訂單谣拣,在CREATE_PENDING狀態(tài)下創(chuàng)建故障單募寨,并發(fā)布TicketCreated事件。
- Accounting服務(wù)消費(fèi)OrderCreated事件并創(chuàng)建一個處于PENDING狀態(tài)的Credit CardAuthorization森缠。
- Accounting Service消費(fèi)TicketCreated和ConsumerVerified事件绪商,收取消費(fèi)者的信用卡,并發(fā)布信用卡授權(quán)活動辅鲸。
- Kitchen Service使用CreditCardAuthorized事件并更改AWAITING_ACCEPTANCE票的狀態(tài)格郁。
- Order Service收到CreditCardAuthorized事件,更改訂單狀態(tài)到APPROVED独悴,并發(fā)布OrderApproved事件例书。
創(chuàng)建訂單saga還必須處理saga參與者拒絕訂單并發(fā)布某種失敗事件的場景。例如刻炒,消費(fèi)者信用卡的授權(quán)可能會失敗决采。saga必須執(zhí)行補(bǔ)償交易以撤消已經(jīng)完成的事情。圖中顯示了AccountingService無法授權(quán)消費(fèi)者信用卡時的事件流坟奥。
事件順序如下:
- Order服務(wù)在APPROVAL_PENDING狀態(tài)下創(chuàng)建一個Order并發(fā)布OrderCreated事件树瞭。
- Consumer服務(wù)消費(fèi)OrderCreated事件,驗(yàn)證消費(fèi)者是否可以下訂單爱谁,并發(fā)布ConsumerVerified事件晒喷。
- Kitchen服務(wù)消費(fèi)OrderCreated事件,驗(yàn)證訂單访敌,在CREATE_PENDING狀態(tài)下創(chuàng)建故障單凉敲,并發(fā)布TicketCreated事件。
- Accounting服務(wù)消費(fèi)OrderCreated事件并創(chuàng)建一個處于PENDING狀態(tài)的Credit CardAuthorization寺旺。
- Accounting服務(wù)消費(fèi)TicketCreated和ConsumerVerified事件爷抓,向消費(fèi)者的信用卡收費(fèi),并發(fā)布信用卡授權(quán)失敗事件阻塑。
- Kitchen服務(wù)使用信用卡授權(quán)失敗事件并將故障單的狀態(tài)更改為REJECTED蓝撇。
- 訂單服務(wù)消費(fèi)信用卡授權(quán)失敗事件,并將訂單狀態(tài)更改為已拒絕陈莽。
可靠的基于事件的通信
在實(shí)施基于編排的saga時渤昌,您必須考慮一些與服務(wù)間通信相關(guān)的問題。第一個問題是確保saga參與者更新其數(shù)據(jù)庫并將事件作為數(shù)據(jù)庫事務(wù)的一部分發(fā)布传透。
您需要考慮的第二個問題是確保saga參與者必須能夠?qū)⑹盏降拿總€事件映射到自己的數(shù)據(jù)耘沼。
編組的saga的好處和缺點(diǎn)
基于編舞的saga有幾個好處:
- 簡單:服務(wù)在創(chuàng)建极颓,更新或刪除業(yè)務(wù)時發(fā)布事件對象
- 松耦合:參與者訂閱事件并且彼此之間沒有直接的了解朱盐。
并且有一些缺點(diǎn):
- 更難理解:與業(yè)務(wù)流程不同,代碼中沒有一個地方可以定義saga菠隆。相反兵琳,編排在服務(wù)中分配saga的實(shí)現(xiàn)狂秘。因此,開發(fā)人員有時很難理解給定的saga是如何工作的躯肌。
- 服務(wù)之間的循環(huán)依賴關(guān)系:saga參與者訂閱彼此的事件者春,這通常會創(chuàng)建循環(huán)依賴關(guān)系。例如清女,如果仔細(xì)檢查圖示钱烟,您將看到存在循環(huán)依賴關(guān)系,例如訂單服務(wù)嫡丙、會計(jì)服務(wù)拴袭、訂單服務(wù)。雖然這不一定是個問題曙博,但循環(huán)依賴性被認(rèn)為是設(shè)計(jì)問題拥刻。
- 緊密耦合的風(fēng)險(xiǎn):每個saga參與者都需要訂閱所有影響他們的事件。例如父泳,會計(jì)服務(wù)必須訂閱導(dǎo)致消費(fèi)者信用卡被收費(fèi)或退款的所有事件般哼。因此,存在一種風(fēng)險(xiǎn)惠窄,即需要與Order Service實(shí)施的訂單生命周期保持同步更新蒸眠。
3.2 控制(Orchestration)
控制是實(shí)現(xiàn)sagas的另一種方式。使用業(yè)務(wù)流程時杆融,您可以定義一個控制類黔宛,其唯一的職責(zé)是告訴saga參與者該做什么。 saga控制使用命令/異步回復(fù)樣式交互與參與者進(jìn)行通信擒贸。
- Order Service首先創(chuàng)建一個Order和一個創(chuàng)建訂單控制器臀晃。之后,路徑的流程如下:
- saga orchestrator向Consumer Service發(fā)送Verify Consumer命令介劫。
- Consumer Service回復(fù)Consumer Verified消息徽惋。
- saga orchestrator向Kitchen Service發(fā)送Create Ticket命令。
- Kitchen Service回復(fù)Ticket Created消息座韵。
- saga協(xié)調(diào)器向Accounting Service發(fā)送授權(quán)卡消息险绘。
- Accounting服務(wù)部門使用卡片授權(quán)消息回復(fù)。
- saga orchestrator向Kitchen Service發(fā)送Approve Ticket命令誉碴。
- saga orchestrator向訂單服務(wù)發(fā)送批準(zhǔn)訂單命令宦棺。
使用狀態(tài)機(jī)建模SAGA ORCHESTRATORS
建模saga orchestrator的好方法是作為狀態(tài)機(jī)。狀態(tài)機(jī)由一組狀態(tài)和一組由事件觸發(fā)的狀態(tài)之間的轉(zhuǎn)換組成黔帕。每個transition都可以有一個action代咸,對于一個saga來說是一個saga參與者的調(diào)用。狀態(tài)之間的轉(zhuǎn)換由saga參與者執(zhí)行的本地事務(wù)的完成觸發(fā)成黄。當(dāng)前狀態(tài)和本地事務(wù)的特定結(jié)果決定了狀態(tài)轉(zhuǎn)換以及執(zhí)行的操作(如果有的話)呐芥。對狀態(tài)機(jī)也有有效的測試策略逻杖。因此,使用狀態(tài)機(jī)模型可以更輕松地設(shè)計(jì)思瘟、實(shí)施和測試荸百。
圖顯示了Create Order Saga的狀態(tài)機(jī)模型。此狀態(tài)機(jī)由多個狀態(tài)組成滨攻,包括以下內(nèi)容:
- Verifying Consumer:初始狀態(tài)够话。當(dāng)處于此狀態(tài)時,該saga正在等待消費(fèi)者服務(wù)部門驗(yàn)證消費(fèi)者是否可以下訂單光绕。
- Creating Ticket:該saga正在等待對創(chuàng)建票證命令的回復(fù)更鲁。
- Authorizing Card:等待Accounting服務(wù)授權(quán)消費(fèi)者的信用卡。
- OrderApproved:表示saga成功完成的最終狀態(tài)奇钞。
- Order Rejected:最終狀態(tài)表明該訂單被其中一方參與者們拒絕澡为。
SAGA ORCHESTRATION和TRANSACTIONAL MESSAGING
基于業(yè)務(wù)流程的saga的每個步驟都包括更新數(shù)據(jù)庫和發(fā)布消息的服務(wù)。例如景埃,Order Service持久保存Order和Create Order Saga orchestrator媒至,并向第一個saga參與者發(fā)送消息。一個saga參與者谷徙,例如Kitchen Service拒啰,通過更新其數(shù)據(jù)庫并發(fā)送回復(fù)消息來處理命令消息。 Order Service通過更新saga協(xié)調(diào)器的狀態(tài)并向下一個saga參與者發(fā)送命令消息來處理參與者的回復(fù)消息完慧。服務(wù)必須使用事務(wù)性消息傳遞谋旦,以便自動更新數(shù)據(jù)庫并發(fā)布消息。
讓我們來看看使用saga編排的好處和缺點(diǎn)屈尼。
基于ORCHESTRATION的SAGAS的好處和缺點(diǎn)
基于編排的saga有幾個好處:
- 更簡單的依賴關(guān)系:編排的一個好處是它不會引入循環(huán)依賴關(guān)系册着。 saga orchestrator調(diào)用saga參與者,但參與者不會調(diào)用orchestrator脾歧。因此甲捏,協(xié)調(diào)器依賴于參與者,但反之亦然鞭执,因此沒有循環(huán)依賴性司顿。
- 較少的耦合:每個服務(wù)都實(shí)現(xiàn)了由orchestrator調(diào)用的API,因此它不需要知道saga參與者發(fā)布的事件兄纺。
- 改善關(guān)注點(diǎn)分離并簡化業(yè)務(wù)邏輯:saga協(xié)調(diào)邏輯本地化在saga協(xié)調(diào)器中大溜。域?qū)ο蟾唵危⑶也涣私馑鼈儏⑴c的saga估脆。例如钦奋,當(dāng)使用編排時,Order類不知道任何saga,因此它具有更簡單的狀態(tài)機(jī)模型锨苏。在執(zhí)行創(chuàng)建訂單saga期間疙教,它直接從APPROVAL_PENDING狀態(tài)轉(zhuǎn)換到APPROVED狀態(tài)棺聊。 Order類沒有與saga的步驟相對應(yīng)的任何中間狀態(tài)伞租。因此,業(yè)務(wù)更加簡單限佩。
業(yè)務(wù)流程也有一個缺點(diǎn):
- 在協(xié)調(diào)器中集中過多業(yè)務(wù)邏輯的風(fēng)險(xiǎn)葵诈。這導(dǎo)致了一種設(shè)計(jì),其中智能協(xié)調(diào)器告訴啞巴服務(wù)要做什么操作祟同。幸運(yùn)的是作喘,您可以通過設(shè)計(jì)獨(dú)立負(fù)責(zé)排序的協(xié)調(diào)器來避免此問題,并且不包含任何其他業(yè)務(wù)邏輯晕城。
除了最簡單的saga泞坦,我建議使用編排。為您的saga實(shí)施協(xié)調(diào)邏輯只是您需要解決的設(shè)計(jì)問題之一砖顷。