分布式事務(wù)處理方案大 PK!
說好了寫 TienChin 項目的,最近這個分布式事務(wù)算是一個支線任務(wù)吧,今天是最后一篇,松哥再來一個短篇和小伙伴們總結(jié)一下分布式事務(wù)。
首先先說一個大原則:分布式事務(wù)能不用就不要用,畢竟這個用起來還是有一些麻煩的。當(dāng)然,不用和不會用可是兩碼事。
1. 分布式事務(wù)基礎(chǔ)理論
學(xué)習(xí)分布式事務(wù),有一些基礎(chǔ)理論需要我們先來了解下。
1.1 本地事務(wù)
本地事務(wù)是指將多條語句作為一個整體進(jìn)行操作的功能,通過數(shù)據(jù)庫事務(wù)可以確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗,如果事務(wù)失敗,那么效果就和沒有執(zhí)行這些SQL一樣,不會對數(shù)據(jù)庫數(shù)據(jù)有任何改動。也就是事務(wù)具有原子性,一個事務(wù)中的一系列操作要么全部成功,要么全部失敗。一般來說,事務(wù)具有 4 個屬性:
- Atomic:原子性,將一個事務(wù)中的所有 SQL 作為原子工作單元執(zhí)行,要么全部執(zhí)行,要么全部不執(zhí)行;
- Consistent:一致性,事務(wù)完成后,所有數(shù)據(jù)的狀態(tài)都是一致的,以銀行轉(zhuǎn)帳為例,如果 A 賬戶減去了 100,則 B 賬戶則必定加上了 100;
- Isolation:隔離性,如果有多個事務(wù)并發(fā)執(zhí)行,每個事務(wù)作出的修改必須與其他事務(wù)隔離;
- Duration:持久性,即事務(wù)完成后,對數(shù)據(jù)庫數(shù)據(jù)的修改被持久化存儲。
這四個屬性通常稱為 ACID 特性。
這塊松哥之前專門錄過相關(guān)的視頻,這里就不再贅述了。
- https://www.bilibili.com/video/BV1Eq4y1R7Ds
1.2 分布式事務(wù)
當(dāng)我們的項目上了微服務(wù)之后,分布式事務(wù)就是一個比較常見的問題了,我們也會遇到很多相關(guān)的場景。
就拿我們前兩天講的商品下單的分布式事務(wù)的案例來說,像下面這樣,一共有五個服務(wù),架構(gòu)如下圖:
- eureka:這是服務(wù)注冊中心。
- account:這是賬戶服務(wù),可以查詢/修改用戶的賬戶信息(主要是賬戶余額)。
- order:這是訂單服務(wù),可以下訂單。
- storage:這是一個倉儲服務(wù),可以查詢/修改商品的庫存數(shù)量。
- bussiness:這是業(yè)務(wù),用戶下單操作將在這里完成。
當(dāng)用戶想要下單的時候,調(diào)用了 bussiness 中的接口,bussiness 中的接口又調(diào)用了它自己的 service,在 service 中,通過 feign 調(diào)用 storage 中的接口去扣庫存,然后再通過 feign 調(diào)用 order 中的接口去創(chuàng)建訂單(order 在創(chuàng)建訂單的時候,不僅會創(chuàng)建訂單,還會扣除用戶賬戶的余額)。
這三個操作,我們希望他們能夠同時成功或者同時失敗。然而如上圖所示,三個微服務(wù)都有自己的 DB,這是三個完全不同的 DB,相當(dāng)于三個不同的本地事務(wù),按照傳統(tǒng)的本地事務(wù)規(guī)則,我們顯然是無法實現(xiàn)三個操作同時成功或者同時失敗的。
想要實現(xiàn) storage、order 以及 account 中的操作同時成功或者同時失敗,就得考慮分布式事務(wù)了。
最后,我們再來看看分布式事務(wù)的概念:分布式事務(wù)是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于的不同節(jié)點之上,數(shù)據(jù)庫的操作執(zhí)行成功與否,不僅取決于本地 DB 的執(zhí)行結(jié)果,也取決于第三方系統(tǒng)的執(zhí)行結(jié)果。而分布式事務(wù)就保證這些操作要么全部成功,要么全部失敗。本質(zhì)上,分布式事務(wù)就是為了保證不同數(shù)據(jù)庫的數(shù)據(jù)一致性。
1.3 CAP
CAP 定理(CAP theorem),有時候又被稱作布魯爾定理(Brewer's theorem),它指出對于一個分布式計算系統(tǒng)來說,不可能同時滿足以下三點:
一致性(Consistency):在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時刻是否具備同樣的值。(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)。
可用性(Availability):在集群中一部分節(jié)點故障后,集群整體是否還能響應(yīng)客戶端的讀寫請求。(對數(shù)據(jù)更新具備高可用性)。
分區(qū)容錯性(Partition tolerance):這個我覺得可能對有的小伙伴來說有點難以理解,我就簡單說一下,先來說分區(qū):因為我們是分布式系統(tǒng),分布式系統(tǒng)中不同的微服務(wù)位于不同的網(wǎng)絡(luò)節(jié)點上,當(dāng)發(fā)生網(wǎng)絡(luò)故障或者節(jié)點故障的時候,不同的服務(wù)之間就無法通信了,也就是說發(fā)生了分區(qū);再來看分區(qū)容錯性:這是說,當(dāng)我們的系統(tǒng)中出現(xiàn)分區(qū)的時候,系統(tǒng)還要能運行,不能罷工!一般來說,在一個分布式系統(tǒng)中,分區(qū)發(fā)生的概率還是比較大的,不會發(fā)生分區(qū)的系統(tǒng),那就不是分布式系統(tǒng)了,而是單體應(yīng)用了。
CAP 原則的精髓就是要么 AP,要么 CP,要么 AC,但是不存在 CAP。因為在分布式系統(tǒng)內(nèi),P 是必然的發(fā)生的,不選 P,一旦發(fā)生分區(qū),整個分布式系統(tǒng)就完全無法使用了,這樣的系統(tǒng)就太脆弱了。所以對于分布式系統(tǒng),我們只能能考慮當(dāng)發(fā)生分區(qū)錯誤時,如何選擇一致性和可用性(選擇一致性,意味著服務(wù)在某段時間內(nèi)不可用,選擇了可用性,意味著服務(wù)雖然一直可用但是返回的數(shù)據(jù)卻不一致)。
而根據(jù)一致性和可用性的選擇不同,開源的分布式系統(tǒng)往往又被分為 CP 系統(tǒng)和 AP 系統(tǒng)。
當(dāng)一套系統(tǒng)在發(fā)生分區(qū)故障后,客戶端的任何請求都被卡死或者超時,但是系統(tǒng)的每個節(jié)點總是會返回一致的數(shù)據(jù),則這套系統(tǒng)就是 CP 系統(tǒng),經(jīng)典的比如 Zookeeper。
如果一套系統(tǒng)發(fā)生分區(qū)故障后,客戶端依然可以訪問系統(tǒng),但是獲取的數(shù)據(jù)有的是新的數(shù)據(jù),有的還是老數(shù)據(jù),那么這套系統(tǒng)就是 AP 系統(tǒng),經(jīng)典的比如 Eureka。
1.4 BASE
因為無法同時滿足 CAP,所以又有了 BASE 理論,BASE 理論指的是:
- 基本可用 Basically Available:分布式系統(tǒng)在出現(xiàn)故障的時候,允許損失部分可用性,即保證核心可用。
- 軟狀態(tài) Soft State:允許系統(tǒng)存在中間狀態(tài),而該中間狀態(tài)不會影響系統(tǒng)整體可用性。
- 終一致性 Eventual Consistency:系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過一定時間后,最終能夠達(dá)到一致的狀態(tài)。
BASE 理論的核心思想是即便無法做到強一致性,但應(yīng)該采用適合的方式保證最終一致性。
BASE 理論本質(zhì)上是對 CAP 理論的延伸,是對 CAP 中 AP 方案的一個補充。
1.5 剛?cè)岵?jì)
事務(wù)有剛性事務(wù)和柔性事務(wù)之分。
剛性事務(wù)(如單數(shù)據(jù)庫中的本地事務(wù))完全遵循 ACID 規(guī)范,即數(shù)據(jù)庫事務(wù)正確執(zhí)行的四個基本要素:
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔離性(Isolation)
- 持久性(Durability)
柔性事務(wù),主要就是只分布式事務(wù)了,柔性事務(wù)為了滿足可用性、性能與降級服務(wù)的需要,降低一致性(Consistency)與隔離性(Isolation)的要求,遵守 BASE 理論:
- 基本業(yè)務(wù)可用性(Basic Availability)
- 柔性狀態(tài)(Soft state)
- 最終一致性(Eventual consistency)
當(dāng)然,柔性事務(wù)也部分遵循 ACID 規(guī)范:
- 原子性:嚴(yán)格遵循
- 一致性:事務(wù)完成后的一致性嚴(yán)格遵循;事務(wù)中的一致性可適當(dāng)放寬
- 隔離性:并行事務(wù)間不可影響;事務(wù)中間結(jié)果可見性允許安全放寬
- 持久性:嚴(yán)格遵循
柔性事務(wù)有不同的分類,不過基本上都可以看作是分布式事務(wù)的解決方案:
- 兩階段型:分布式事務(wù)二階段提交,對應(yīng)技術(shù)上的 XA、JTA/JTS,這是分布式環(huán)境下事務(wù)處理的典型模式。
- 補償型:我們之前文章介紹的 TCC,就算是一種補償型事務(wù),在 Try 成功的情況下,如果事務(wù)要回滾,Cancel 將作為一個補償機制,回滾 Try 操作;TCC 各操作事務(wù)本地化,且盡早提交(沒有兩階段約束);當(dāng)全局事務(wù)要求回滾時,通過另一個本地事務(wù)實現(xiàn)“補償”行為。TCC 是將資源層的二階段提交協(xié)議轉(zhuǎn)換到業(yè)務(wù)層,成為業(yè)務(wù)模型中的一部分。
- 異步確保型:將一些有同步?jīng)_突的事務(wù)操作變?yōu)楫惒讲僮?,避免對?shù)據(jù)庫事務(wù)的爭用,如消息事務(wù)機制。
- 最大努力通知型:通過通知服務(wù)器(消息通知)進(jìn)行,允許失敗,有補充機制。
2. 分布式事務(wù)實踐
2.1 XA
先來說說 XA。
XA 是一種典型的兩階段提交(2PC,Two-phase commit protocol),而兩階段提交是一種強一致性設(shè)計,在兩階段提交中,一般會引入一個事務(wù)協(xié)調(diào)者的角色來協(xié)調(diào)管理各個事務(wù)參與者,例如我們之前文章中使用的 seata-server 其實是就是一個事務(wù)協(xié)調(diào)者。所謂的兩階段分別指的是準(zhǔn)備和提交兩個階段。
XA 規(guī)范 是 X/Open 組織定義的分布式事務(wù)處理(DTP,Distributed Transaction Processing)標(biāo)準(zhǔn)。
XA 規(guī)范描述了全局的事務(wù)管理器與局部的資源管理器之間的接口。XA規(guī)范的目的是允許多個資源(如數(shù)據(jù)庫,應(yīng)用服務(wù)器,消息隊列等)在同一事務(wù)中訪問,這樣可以使 ACID 屬性跨越應(yīng)用程序而保持有效。
XA 規(guī)范使用兩階段提交來保證所有資源同時提交或回滾任何特定的事務(wù)。
XA 規(guī)范在上世紀(jì) 90 年代初就被提出。目前,幾乎所有主流的數(shù)據(jù)庫如 MySQL、Oracle、MSSQL 等都對 XA 規(guī)范提供了支持。
XA 事務(wù)的基礎(chǔ)是兩階段提交協(xié)議。需要有一個事務(wù)協(xié)調(diào)者來保證所有的事務(wù)參與者都完成了準(zhǔn)備工作(第一階段)。如果協(xié)調(diào)者收到所有參與者都準(zhǔn)備好的消息,就會通知所有的事務(wù)都可以提交了(第二階段)。MySQL 在這個 XA 事務(wù)中扮演的是參與者的角色,而不是協(xié)調(diào)者(事務(wù)管理器)。
MySQL 的 XA 事務(wù)分為內(nèi)部 XA 和外部 XA。外部 XA 可以參與到外部的分布式事務(wù)中,需要應(yīng)用層介入作為協(xié)調(diào)者;內(nèi)部 XA 事務(wù)用于同一實例下跨多引擎事務(wù),由 Binlog 作為協(xié)調(diào)者,比如在一個存儲引擎提交時,需要將提交信息寫入二進(jìn)制日志,這就是一個分布式內(nèi)部 XA 事務(wù),只不過二進(jìn)制日志的參與者是 MySQL 本身。MySQL 在 XA 事務(wù)中扮演的是一個參與者的角色,而不是協(xié)調(diào)者。
XA 事務(wù)的特點是:
- 簡單易理解,開發(fā)較容易。
- 對資源進(jìn)行了長時間的鎖定,并發(fā)度低。
2.2 3PC
3PC 主要是為了彌補 2PC 的不足而產(chǎn)生的,2PC 有哪些不足呢?
- 同步阻塞:2PC 在執(zhí)行過程中,所有參與節(jié)點(也就是一個分支事務(wù))都是事務(wù)阻塞型的,當(dāng)參與者占有公共資源時,其他第三方節(jié)點訪問公共資源不得不處于阻塞狀態(tài),也就是在 2PC 執(zhí)行的過程中,資源是被鎖住的。
- 單點故障:在 2PC 中,事務(wù)協(xié)調(diào)者扮演了舉足輕重的作用,由于事務(wù)協(xié)調(diào)者的重要性,一旦事務(wù)協(xié)調(diào)者發(fā)生故障,事務(wù)的參與者就會一直阻塞下去。尤其是在第二階段,如果協(xié)調(diào)者發(fā)生故障,那么所有的參與者還都處于鎖定事務(wù)資源的狀態(tài)中,而無法繼續(xù)完成事務(wù)操作。還有一個問題,就是當(dāng)事務(wù)協(xié)調(diào)者發(fā)出 commit 指令之前,如果宕機了,此時雖然可以重新選舉一個新的協(xié)調(diào)者出來,但是還是無法解決因為事務(wù)協(xié)調(diào)者宕機導(dǎo)致的事務(wù)參與者處于阻塞狀態(tài)的問題。
3PC 則嘗試解決 2PC 的這些問題。3PC 主要是把 2PC 中的第一階段再次一分為二,這樣 3PC 就有 CanCommit、PreCommit 以及 DoCommit 三個不同的階段。不過 3PC 并不能解決 2PC 的所有問題,3PC 主要解決了單點故障問題,并且減少了阻塞。一旦事務(wù)參與者(分支事務(wù))無法及時收到來自事務(wù)協(xié)調(diào)者的信息,那么分支事務(wù)會默認(rèn)執(zhí)行 commit,而不會一直持有事務(wù)資源并處于阻塞狀態(tài),不過這種機制也帶來了新的問題,假設(shè)事務(wù)協(xié)調(diào)者發(fā)送了 abort 指令給各個分支事務(wù),然而由于網(wǎng)絡(luò)問題導(dǎo)致分支事務(wù)沒有及時接收到該指令,那么分支事務(wù)在等待超時之后執(zhí)行了 commit 操作,這樣就和其他接到 abort 命令并執(zhí)行回滾的分支事務(wù)之間存在數(shù)據(jù)不一致的情況。
我們來看看 3PC 的流程:
- CanCommit 階段:這個階段所做的事很簡單,就是事務(wù)協(xié)調(diào)者詢問各個分支事務(wù),你是否有能力完成此次事務(wù)?如果都返回 yes,則進(jìn)入第二階段;有一個返回 no 或等待響應(yīng)超時,則中斷事務(wù),并向所有分支事務(wù)發(fā)送 abort 請求。
- PreCommit 階段:此時事務(wù)協(xié)調(diào)者會向所有的分支事務(wù)發(fā)送 PreCommit 請求,分支事務(wù)收到后開始執(zhí)行事務(wù)操作,并將 Undo 和 Redo 信息記錄到事務(wù)日志中。分支執(zhí)行完事務(wù)操作后(此時屬于未提交事務(wù)的狀態(tài)),就會向事務(wù)協(xié)調(diào)者反饋“Ack”表示我已經(jīng)準(zhǔn)備好提交了,并等待協(xié)調(diào)者的下一步指令。
- DoCommit 階段:在階段二中如果所有的分支事務(wù)節(jié)點都可以進(jìn)行 PreCommit 提交,那么事務(wù)協(xié)調(diào)者就會從“預(yù)提交狀態(tài)”轉(zhuǎn)變?yōu)椤疤峤粻顟B(tài)”,然后向所有的分支事務(wù)節(jié)點發(fā)送"doCommit"請求,分支事務(wù)節(jié)點在收到提交請求后就會各自執(zhí)行事務(wù)提交操作,并向協(xié)調(diào)者節(jié)點反饋“Ack”消息,協(xié)調(diào)者收到所有參與者的 Ack 消息后完成事務(wù)。
相反,如果有一個分支事務(wù)節(jié)點未完成 PreCommit 的反饋或者反饋超時,那么協(xié)調(diào)者都會向所有的參與者節(jié)點發(fā)送 abort 請求,從而中斷事務(wù)。
2.3 TCC
關(guān)于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。
TCC 模式主要有如下一些優(yōu)缺點:
優(yōu)點:
- 性能提升:通過具體業(yè)務(wù)來實現(xiàn)控制資源鎖的粒度變小,不會鎖定整個資源。
- 數(shù)據(jù)最終一致性:基于 Confirm 和 Cancel 的冪等性,保證事務(wù)最終完成確認(rèn)或者取消,保證數(shù)據(jù)的一致性。
- 可靠性:解決了 XA 協(xié)議的協(xié)調(diào)者單點故障問題,由主業(yè)務(wù)方發(fā)起并控制整個業(yè)務(wù)活動,業(yè)務(wù)活動管理器也變成多點,引入集群。
缺點:
- 對微服務(wù)的侵入性強,微服務(wù)的每個事務(wù)都必須實現(xiàn) try,confirm,cancel 等 3 個方法,開發(fā)成本高,今后維護(hù)改造的成本也高。
- 為了達(dá)到事務(wù)的一致性要求,try,confirm、cancel 接口必須實現(xiàn)等冪性操作,這在一定程度上增加了開發(fā)工作量。
TCC 主要是兩個階段,步驟如下:
- Try 階段(一階段):嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性), 預(yù)留必須業(yè)務(wù)資源(準(zhǔn)隔離性)。
- Confirm 階段(二階段):確認(rèn)執(zhí)行真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源,Confirm 操作滿足需要滿足冪等性,Confirm 執(zhí)行失敗后需要進(jìn)行重試。
- Cancel 階段:取消執(zhí)行,釋放 Try 階段預(yù)留的業(yè)務(wù)資源,Cancel 操作也需要滿足冪等性。Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致。
在我們之前的文章中,松哥也給大家舉了 TCC 的例子了,這里就不再贅述了。
2.4 SAGA
SAGA 最初出現(xiàn)在 1987 年 Hector Garcaa-Molrna & Kenneth Salem 發(fā)表的論文 SAGAS 里。這篇論文的核心思想是將長事務(wù)拆分為多個短事務(wù),由 Saga 事務(wù)協(xié)調(diào)器協(xié)調(diào),如果每個短事務(wù)都成功提交完成,那么全局事務(wù)就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。
Saga 事務(wù)的特點是:
- 并發(fā)度高,不用像 XA 事務(wù)那樣長期鎖定資源。
- 需要定義正常操作以及補償操作(回滾),開發(fā)量工作量比 XA 大。
- 一致性較弱,對于轉(zhuǎn)賬,可能發(fā)生 A 用戶已扣款,最后轉(zhuǎn)賬又失敗的情況
SAGA 適用的場景較多,適用于長事務(wù)或者對中間結(jié)果不敏感的業(yè)務(wù)場景。
2.5 本地消息表
本地消息表這個方案最初是 ebay 架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章中提出。
顧名思義,本地消息表就是會有一張存放本地消息的表,一般都是放在數(shù)據(jù)庫中,然后在執(zhí)行業(yè)務(wù)的時候?qū)I(yè)務(wù)的執(zhí)行和將消息放入消息表中的操作放在同一個事務(wù)中,這樣就能保證消息放入本地表以及業(yè)務(wù)肯定是一起執(zhí)行成功的。
當(dāng)一個操作執(zhí)行成功之后,再去執(zhí)行下一個操作,如果下一個操作調(diào)用成功了好說,消息表的消息狀態(tài)可以直接改為已成功;如果下一個任務(wù)調(diào)用失敗也沒關(guān)系,會有后臺任務(wù)定時去讀取本地消息表,篩選出還未成功的消息再調(diào)用對應(yīng)的服務(wù)(重試),服務(wù)更新成功了再變更消息的狀態(tài)。
重試就得保證對應(yīng)服務(wù)的方法是冪等的,而且一般重試會有最大次數(shù),超過最大次數(shù)可以記錄下報警讓人工處理。
根據(jù)上面的描述,小伙伴們其實可以看到,本地消息表其實實現(xiàn)的是最終一致性,容忍了數(shù)據(jù)暫時不一致的情況。
本地消息表的特點:
- 長事務(wù)僅需要分拆成多個任務(wù),使用簡單。
- 生產(chǎn)者需要額外的創(chuàng)建消息表。
- 每個本地消息表都需要進(jìn)行輪詢(如果有失敗的要重試)。
- 消費者的邏輯如果無法通過重試成功,那么還需要更多的機制,來回滾操作。
根據(jù)本地消息表的特點我們可以發(fā)現(xiàn),本地消息表適用于可異步執(zhí)行且后續(xù)操作無需回滾的業(yè)務(wù)。
2.6 消息事務(wù)
這種方案的核心思路,其實就是通過消息中間件來將全局事務(wù)轉(zhuǎn)為本地事務(wù),通過消息中間件來確保各個分支事務(wù)最終都能調(diào)用成功。
不過后來發(fā)現(xiàn)利用 Alibaba 的 RocketMQ(4.3之后)可以更好的實現(xiàn)分布式事務(wù)。
RocketMQ 是一種最終一致性的分布式事務(wù),就是說它保證的是消息最終一致性,而不是像 2PC、3PC、TCC 那樣強一致分布式事務(wù),在 RocketMQ 中有一種消息叫做 Half Message,Half Message 是指暫不能被 Consumer 消費的消息,雖然 Producer 已經(jīng)把消息成功發(fā)送到了 Broker 端,但此消息被標(biāo)記為暫不能投遞狀態(tài),處于該種狀態(tài)下的消息稱為半消息,此時需要 Producer 對消息進(jìn)行二次確認(rèn)后,Consumer 才能去消費它。
RocketMQ 就是基于 Half Message 來實現(xiàn)的分布式事務(wù),舉一個轉(zhuǎn)賬的例子:
- A 服務(wù)先發(fā)送個 Half Message 給 Brock 端,消息中攜帶 B 服務(wù)即將要 +100 元的信息。
- 當(dāng) A 服務(wù)知道 Half Message 發(fā)送成功后,那么開始本地事務(wù)。
- 執(zhí)行本地事務(wù)(會有三種情況1、執(zhí)行成功;2、執(zhí)行失??;3、網(wǎng)絡(luò)等原因?qū)е聸]有響應(yīng)) 3.1 如果本地事務(wù)成功,那么 A 向 Broker 服務(wù)器發(fā)送 Commit,這樣 B 服務(wù)就可以消費該 message。3.2 如果本地事務(wù)失敗,那么 A 向 Broker 服務(wù)器發(fā)送 Rollback,那么就會直接刪除上面這條半消息。3.3 如果由于網(wǎng)絡(luò)或者生產(chǎn)者應(yīng)用重啟等原因。導(dǎo)致 A 一直沒有對 Half Message 進(jìn)行二次確認(rèn),此時 Broker 服務(wù)器會定時掃描長期處于半消息的消息,會主動詢問 A 端該消息的最終狀態(tài)(Commit 或者 Rollback),這個操作也就是所謂的消息回查。
可能有小伙伴會說,那要是 B 最終執(zhí)行失敗怎么辦?對于這種情況,我們幾乎可以斷定就是代碼有問題所以才引起異常,因為消費端 RocketMQ 有重試機制,如果不是代碼問題一般重試幾次就能成功。
如果是代碼的原因引起多次重試失敗后,也沒有關(guān)系,將該異常記錄下來,由人工處理,人工兜底處理后,就可以讓事務(wù)達(dá)到最終的一致性。
2.7 最大努力通知
發(fā)起通知方通過一定的機制最大努力將業(yè)務(wù)處理結(jié)果通知到接收方。具體包括:
- 有一定的消息重試機制。因為接收通知方可能沒有接收到通知,此時要有一定的機制對消息進(jìn)行重試。
- 消息校對機制。如果盡最大努力也沒有通知到接收方,或者接收方消費消息后要再次消費,此時可由接收方主動向通知方查詢消息信息來滿足需求。
在前面兩個小節(jié)介紹的的本地消息表和事務(wù)消息都屬于可靠消息,這與我們這里介紹的最大努力通知有什么不同?
- 可靠消息一致性:消息發(fā)起方需要保證將消息發(fā)出去,并且將消息發(fā)到接收方,消息的可靠性關(guān)鍵由發(fā)起方來保證。
- 最大努力通知:消息發(fā)起方盡最大努力將業(yè)務(wù)處理結(jié)果通知給接收方,但是可能消息接收不到,此時需要接收方主動調(diào)用發(fā)起方的接口查詢業(yè)務(wù)處理結(jié)果,此時消息的可靠性關(guān)鍵在接收方。
僅此而已。
在具體的解決方案上,最大努力通知需要消息發(fā)起方提供接口,讓被通知方能夠通過接口查詢業(yè)務(wù)處理結(jié)果。
最大努力通知適用于業(yè)務(wù)通知類型,最常見的場景就是支付回調(diào),支付服務(wù)收到第三方服務(wù)支付成功通知后,先更新自己庫中訂單支付狀態(tài),然后同步通知訂單服務(wù)支付成功。如果此次同步通知失敗,會通過異步腳步不斷重試地調(diào)用訂單服務(wù)的接口。
最大努力通知更多是業(yè)務(wù)上的設(shè)計,在基礎(chǔ)設(shè)施層,可以直接使用二階段消息,或者事務(wù)消息、本地消息表等來實現(xiàn)。
3. 小結(jié)
好啦,學(xué)習(xí)分布式事務(wù)解決方案,最大的感受就是:沒有銀彈!
參考資料:
https://help.aliyun.com/document_detail/132895.html
https://cloud.tencent.com/developer/article/1860632
https://zh.m.wikipedia.org/zh-hans/CAP%E5%AE%9A%E7%90%86
https://zhuanlan.zhihu.com/p/35616811