微服務(wù)架構(gòu)的數(shù)據(jù)一致性微服務(wù)架構(gòu)下,,最好的分布式數(shù)據(jù)一致性解決方案就是盡量避免分布式事務(wù),,然而,在很多場景下,,分布式事務(wù)是難以避免的,。在金融、電信領(lǐng)域中,,很多業(yè)務(wù)場景要求數(shù)據(jù)的強一致性,,同時要保證服務(wù)的可擴展性和可靠性。如何保證分布式事務(wù)下的數(shù)據(jù)一致性成為微服務(wù)架構(gòu)的一個重要課題和難點,。 解決方案概覽在工程領(lǐng)域,,分布式事務(wù)的討論主要聚焦于強一致性和最終一致性的解決方案。常見的分布式事務(wù)有基于XA協(xié)議的兩階段(2PC)提交模式,,以及改良版本的三階段(3PC)提交模式,。Java事務(wù)編程接口(Java Transaction API,JTA)和Java事務(wù)服務(wù)(Java TransactionService,,JTS)正是基于XA協(xié)議的實現(xiàn),。分布式事務(wù)包括事務(wù)管理器TM(Transaction Manager)和一個或多個支持XA協(xié)議的資源管理器RM(Resource Manager)。在微服務(wù)架構(gòu)下,,我們傾向使用最終一致性的方案,。下面將介紹2PC模式、TCC模式,、Saga模式等,。 ● 2PC模式,分布式事務(wù)比較典型的解決方案,,但是對于微服務(wù)架構(gòu)而言,,可能這一方案并不適用,主要原因是不同微服務(wù)可能使用的數(shù)據(jù)存儲類型不同,。如果使用的NoSQL不支持事務(wù)的數(shù)據(jù)庫,,那么事務(wù)根本無法實現(xiàn)2PC模式,。此外,,2PC模式本身也存在同步阻塞、單點故障和性能問題,。 ● TCC模式,,相比2PC模式,具有更強的靈活性和性能優(yōu)勢,TCC模式本質(zhì)上是基于服務(wù)層的2PC編程模式,,把事務(wù)從數(shù)據(jù)庫層的事務(wù)操作邏輯抽象到業(yè)務(wù)服務(wù)層,,通過服務(wù)層業(yè)務(wù)邏輯實現(xiàn)服務(wù)的補償模式。 ● Saga模式,,核心理念是將事務(wù)切分成一組依次執(zhí)行的短事務(wù),,也可以理解成基于業(yè)務(wù)補償邏輯實現(xiàn)分布式下的高性能分布式事務(wù)模式。較之基于單一數(shù)據(jù)庫資源訪問的本地事務(wù),,分布式事務(wù)的應(yīng)用架構(gòu)更為復(fù)雜,。在不同的分布式應(yīng)用架構(gòu)下,實現(xiàn)一個分布式事務(wù)要考慮的問題并不完全一樣,,比如對多資源的協(xié)調(diào),、事務(wù)的跨服務(wù)傳播、業(yè)務(wù)的侵入性,、隔離性等,,實現(xiàn)機制也是復(fù)雜多變的。 ● 可靠消息模式的解決方案是使用消息隊列進行系統(tǒng)間的解耦,。 由上游服務(wù)發(fā)起事件,,通過消息隊列傳遞到下游服務(wù),下游服務(wù)接收到消息后進行事件消費,,最終完成業(yè)務(wù),,達到數(shù)據(jù)一致。為了解決消息隊列及上下游服務(wù)的不可靠性,,通常還會借助額外的事件表和定時器輔助完成數(shù)據(jù)補償,。 兩階段提交模式2PC(兩階段提交) 2PC是一個非常經(jīng)典的強一致、中心化的通過原子提交來實現(xiàn)分布式事務(wù)一致性管理的協(xié)議,。這里所說的中心化指協(xié)議中有兩類節(jié)點: 中 心 化 協(xié) 調(diào) 者 節(jié) 點 ( Coordinator ) 和 N 個 參 與 者 節(jié) 點(Participant),。當(dāng)一個事務(wù)跨越多個節(jié)點時,為了保持事務(wù)的ACID特性,,需要協(xié)調(diào)者統(tǒng)一掌控所有節(jié)點(又稱作參與者)的操作結(jié)果,,并最終指示這些節(jié)點是否要把操作結(jié)果進行真正的提交或回滾。 2PC將整個事務(wù)流程分為兩個階段:準(zhǔn)備階段(Prepare Phase)和提交階段(Commit Phase),。整個事務(wù)過程由事務(wù)管理器和參與者組成,,事務(wù)管理器負(fù)責(zé)決策整個分布式事務(wù)的提交和回滾,事務(wù)參與者負(fù)責(zé)自己本地事務(wù)的提交和回滾,。大部分關(guān)系數(shù)據(jù)庫,,如Oracle、MySQL,,都支持兩階段提交協(xié)議,。下面是計算機數(shù)據(jù)庫進行兩階段提交的說明。 ● 準(zhǔn)備階段:事務(wù)管理器為每個參與者準(zhǔn)備(Prepare)消息,每個參與者在本地執(zhí)行事務(wù),,并寫本地的Undo/Redo日志,,此時事務(wù)沒有被提交。(Undo日志記錄修改的數(shù)據(jù),,用于數(shù)據(jù)回滾,;Redo日志記錄修改后的數(shù)據(jù),用于提交事務(wù)后寫入數(shù)據(jù)文件),。 ● 提交階段:如果事務(wù)管理器收到了參與者執(zhí)行失敗或者超時的消息,,則直接向每個參與者發(fā)送回滾消息;否則,,發(fā)送提交消息,。參與者根據(jù)事務(wù)管理器的消息執(zhí)行提交或者回滾操作,并釋放事務(wù)處理過程中使用的鎖資源,。 2PC的算法思路可以概括為:參與者將操作成敗的結(jié)果通知協(xié)調(diào)者,,再由協(xié)調(diào)者根據(jù)所有參與者的反饋情報決定各參與者是執(zhí)行提交操作還是回滾操作。兩階段提交就是分成兩個階段提交,,第一階段詢問各個事務(wù)數(shù)據(jù)源是否準(zhǔn)備好,,第二階段才真正將數(shù)據(jù)提交給事務(wù)數(shù)據(jù)源。但因為2PC協(xié)議成本比較高,,又有全局鎖的問題,,性能會比較差。現(xiàn)在我們基本上不會采用這種強一致性解決方案,。2PC流程如下圖所示,。 在2PC中,如果兩階段出現(xiàn)協(xié)調(diào)者和參與者都宕機的情況,,則有可能出現(xiàn)數(shù)據(jù)不一致的問題,,同時還存在著諸如同步阻塞、單點問題,、腦裂等問題,,所以研究者們在2PC的基礎(chǔ)上做了改進,提出了3PC,。 3PC(三階段提交) 3PC是2PC的改進版本,,流程如下圖所示。 3PC要解決的最關(guān)鍵問題就是協(xié)調(diào)者和參與者同時宕機的問題,,所以3PC將2PC的第一階段一分為二,,形成了由canCommit、preCommit和Commit 3個階段組成的事務(wù)處理協(xié)議,。 3PC的核心理念是:在詢問時并不鎖定資源,,除非所有參與者都同意了,才開始鎖資源,。一旦參與者無法及時收到來自協(xié)調(diào)者的信息,,它就會默認(rèn)執(zhí)行Commit,而不會一直持有事務(wù)資源并處于阻塞狀態(tài),,但是這種機制也會導(dǎo)致數(shù)據(jù)一致性問題,。3PC在2PC的基礎(chǔ)上做了如下改進: ● 增加了超時機制。 ● 在兩階段之間插入了準(zhǔn)備階段,。 TCC補償模式關(guān)于TCC(Try-Confirm-Cancel)的概念,,最早是由Pat Helland在2007年發(fā)表的一篇名為Lifebeyond Distributed Transactions:anApostate’s Opinion的論文中提出的。在該論文中,,TCC還是以 TCC的核心思想是:針對每個操作都要注冊一個與其對應(yīng)的確認(rèn)和補償(撤銷)操作。TCC事務(wù)處理流程和2PC類似,,不過2PC通常都在跨庫的DB層面,,而TCC本質(zhì)上就是一個應(yīng)用層面的2PC,需要通過業(yè)務(wù)邏輯來實現(xiàn),。 TCC模式的工作原理 TCC模式的工作原理如下圖所示,。TCC將一個完整的事務(wù)提交分為Try、Confirm,、Cancel 3個操作,。 ● Try:預(yù)留業(yè)務(wù)資源/數(shù)據(jù)效驗。 ● Confirm:確認(rèn)執(zhí)行真正要執(zhí)行的業(yè)務(wù),,如果所有事務(wù)參與者的Try操作都執(zhí)行成功了,,就會調(diào)用所有事務(wù)參與者的Confirm操作,確認(rèn)資源,。Confirm操作滿足冪等性,,要求具備冪等設(shè)計,Confirm失敗后需要進行重試,。 ● Cancel:取消執(zhí)行,,如果有事務(wù)參與者在Try階段執(zhí)行失敗,就調(diào)用所有已成功執(zhí)行Try階段的參與者的Cancel方法,,釋放Try階段占用的資源,。Cancel操作滿足冪等性,Cancel階段的異常和Confirm階段的異常處理方案基本上一致,。 下面是實際模擬用戶下單的一個業(yè)務(wù)場景使用TCC模式的主要流程圖,。TCC第一階段是圖中的粗線條部分,這個階段需要分別執(zhí)行訂單服務(wù)和庫存服務(wù)的Try事務(wù),,預(yù)留必需的業(yè)務(wù)資源,;在第一階段執(zhí)行成功后,,就會進入第二階段的Confirm操作,如果不成功,,則進行Cancel操作,。 TCC的優(yōu)點和缺點 ● TCC的優(yōu)點:讓應(yīng)用自己定義數(shù)據(jù)庫操作的粒度,使降低鎖沖突,、提高吞吐量成為可能,。TCC的特點在于業(yè)務(wù)資源檢查與加鎖,一階段進行校驗,,鎖定資源,,如果第一階段都成功,則第二階段對鎖定資源進行交易邏輯,,否則對鎖定資源進行釋放,,這樣就避免了數(shù)據(jù)庫兩階段提交中的鎖沖突和長事務(wù)低性能風(fēng)險。 ● TCC的缺點:業(yè)務(wù)邏輯的每個分支都需要實現(xiàn)Try,、Confirm,、Cancel 3個操作,應(yīng)用侵入性較強,,改造成本高,。另外,實現(xiàn)難度較大,,需要按照網(wǎng)絡(luò)狀態(tài),、系統(tǒng)故障等不同的失敗原因?qū)崿F(xiàn)不同的回滾策略。為了滿足一致性的要求,,Confirm和Cancel接口必須實現(xiàn)冪等,。 TCC的開源解決方案 目前,國內(nèi)的螞蟻金服主要采用TCC模式進行分布式事務(wù)管理,。下面總結(jié)了常用的TCC開源分布式管理框架: ● Seata ● Tcc-transaction ● Hmily ● ByteTCC ● EasyTransaction Saga長事務(wù)模式1987年,,普林斯頓大學(xué)的Hector Garcia-Molina和Kenneth Salem發(fā)表了一篇論文:Sagas[3]。這篇論文提出了使用Saga機制作為分布式事 務(wù) 的 替 代 品 ,, 以 解 決 長 時 間 運 行 的 分 布 式 事 務(wù) ( long-LivedTransaction,,LLT)問題,LLT指長時間持有數(shù)據(jù)庫資源的長活事務(wù),。 該論文認(rèn)為業(yè)務(wù)過程經(jīng)常由很多步驟組成,,每一個步驟都涉及一個事務(wù),如果將這些事務(wù)組成一個分布式事務(wù),,就可以實現(xiàn)總體一致,。然而在長時間運行的分布式事務(wù)中,使用分布式事務(wù)會影響效率和系統(tǒng)的并發(fā)處理能力,,因為在執(zhí)行分布式事務(wù)時會有鎖產(chǎn)生,。Saga通過確保每一個業(yè)務(wù)過程都有修正事務(wù)來減少系統(tǒng)對分布式事務(wù)的依賴,。這種在業(yè)務(wù)流程中執(zhí)行修正事務(wù)的方式最終保證了系統(tǒng)數(shù)據(jù)一致性。 每一個LLT的Saga都由一系列sub-transaction Ti組成,,每個Ti都有對應(yīng)的補償動作Ci,,補償動作用于撤銷Ti造成的結(jié)果。同時Saga定義了兩種恢復(fù)策略: ● 向后恢復(fù)模式(Backward Recovery),,在這種模式下,每一個內(nèi)部子事務(wù)都有一個對應(yīng)的補償事務(wù),,如果任一子事務(wù)失敗,,則將撤銷之前所有成功的sub-transaction,使整個Saga的執(zhí)行結(jié)果都撤銷,。 ● 向前恢復(fù)模式(Forward Recovery),,這種模式假設(shè)每個子事務(wù)最終都會成功,適用于必須要成功的場景,,執(zhí)行順序如下:T1,,T2,…,,Tj(失?。琓j(重試),,…,,Tn,其中Tj是發(fā)生錯誤的sub-transaction,,此時執(zhí)行重試邏輯,,在該模式下不需要執(zhí)行補償事務(wù)。 微服務(wù)架構(gòu)大師Chris Richardson在介紹微服務(wù)架構(gòu)與數(shù)據(jù)一致性技術(shù)時,,提出了Saga模式,,將微服務(wù)中的分布式事務(wù)劃分為一組小的事務(wù),所劃分事務(wù)或者全部提交,,或者全部回滾,。強調(diào)在微服務(wù)中,每個單獨的微服務(wù)都可以確保ACID,,因為每一個微服務(wù)都具備自己的數(shù)據(jù)庫,,但是,Saga模式作為整體并不保證隔離性,,所以需要對異常情況進行補償操作,。 在Saga模式中,為了保障事務(wù)提交和回滾,,應(yīng)使用事務(wù)日志結(jié)尾和消息傳遞的方式,。在發(fā)消息之前,,將消息寫入本地數(shù)據(jù)庫,這就是所謂的事務(wù)日志結(jié)尾,。它的作用是當(dāng)新的日志到來時,,可以直接發(fā)布,這樣就可以強化ACD特性,;同時Saga模式中子模塊之間的消息通信建議采用消息傳遞方式,,因為和HTTP通信協(xié)議相比,消息傳遞方式具備持久性,。 Saga模式的工作原理 在微服務(wù)架構(gòu)下,,Saga存在兩種協(xié)調(diào)模式。 ● 編排模式(Choreography) 這種模式在微服務(wù)之間傳遞Saga,,沒有重協(xié)調(diào)器,,每個微服務(wù)都監(jiān)聽其他服務(wù),并決定采取行動,。這種模式最大的優(yōu)勢就是簡單,,容易理解,參與者之間松散耦合,,對于參與者較少的情況,,適合采用這種模式。下面是使用編排模式的分布式事務(wù)時序圖,,實線表示消息發(fā)布,,虛線表示訂閱。 事件執(zhí)行順序如下: (1)訂單服務(wù)(Order Service)在Approval_pending狀態(tài)下創(chuàng)建了訂單,,并發(fā)布訂單創(chuàng)建事件,。 (2)庫存服務(wù)(Inventory Service)消費訂單創(chuàng)建事件,在Inventory_pending狀態(tài)下驗證訂單,,訂單出庫,,并創(chuàng)建InventoryCreate Event。 (3)賬戶服務(wù)(Account Service)消費訂單創(chuàng)建事件并進入等待的狀態(tài),。 (4)賬戶服務(wù)消費Inventory Event,,收取費用并發(fā)布AccountEvent。 ( 5 ) 庫 存 服 務(wù) 訂 閱 Account Event 商 品 出 庫 ,, 更 改Inventory_pending狀態(tài)為Accept狀態(tài),。 (6)訂單服務(wù)收到Account Event,訂單狀態(tài)改為Approved狀態(tài),。 (7)如果上述訂單服務(wù)失敗,,那么庫存服務(wù)消費費用收取失敗事件,回滾之前庫存事務(wù)清單,,將狀態(tài)改為拒絕,。 (8)如果上述賬戶服務(wù)失敗,,那么訂單服務(wù)消費費用收取失敗事件,回滾訂單狀態(tài)改為拒絕,。 ● 編制模式(Orchestrator) 這種模式需要一個集中的服務(wù)觸發(fā)器,,跟蹤Saga的所有子任務(wù)調(diào)用情況,根據(jù)調(diào)用情況來決定是否采用補償措施,。這種中央?yún)f(xié)調(diào)的方式可以減少每一個微服務(wù)之間的循環(huán)依賴,,集成處理事件決策和邏輯排序,也更容易推理Saga組合,,保證長事務(wù)數(shù)據(jù)的最終一致性,。使用業(yè)務(wù)流程時,可以定義一個控制類,,其唯一職責(zé)是告訴Saga參與者該做什么,。Saga控制使用命令或異步回復(fù)樣式交互與參與者進行通信,。 下面是使用編制模式的分布式事務(wù)時序圖,,實線表示消息request,虛線表示消息reply,。 事件執(zhí)行順序如下: (1)訂單服務(wù)(Order Service)創(chuàng)建一個訂單和一個訂單控制器:Saga Orchestrator,。 (2)Saga Orchestrator向庫存服務(wù)(Inventory Service)發(fā)送一個創(chuàng)建訂單命令。 (3)庫存服務(wù)回復(fù)庫存出庫命令,。 (4)Saga Orchestrator向賬號服務(wù)(Account Service)發(fā)送一個費用收取命令,。 (5)賬號服務(wù)回復(fù)訂單費用扣除命令。 (6)Saga Orchestrator向訂單服務(wù)發(fā)送一個訂單已批準(zhǔn)命令,。
編排模式與編制模式 微服務(wù)中推薦使用編制模式。相比編排模式,,編制模式有下面幾個優(yōu)勢,。 ● 編制模式有更簡化的依賴關(guān)系。編排模式中,,服務(wù)之間需要掌握相互依賴關(guān)系,,而且針對不同的異常,,可能還需要采用不同的補償措施,Saga Orchestrator則調(diào)用Saga參與者,,所有參與者之間是不需要了解Orchestrator的實現(xiàn)細(xì)節(jié)的,。 ● 每個服務(wù)只需要暴露各自的API供Orchestrator調(diào)用即可,因此每個微服務(wù)之間有較少耦合,,可以降低每個參與者的復(fù)雜度,。 ● 可以使用Saga Orchestrator更加方便地增加或減少Saga分布式事務(wù)邏輯,不需要改變每一個參與者的Saga內(nèi)部事務(wù),。同時可以基于Orchestrator進行關(guān)注點分離,,并簡化業(yè)務(wù),操作更加容易,、方便,。 Saga與ACID Saga不提供對整體隔離性的保證。一個長事務(wù)劃分為若干本地事務(wù),,在本地事務(wù)提交后,,整個Saga未完成之前,其他服務(wù)可以訪問“未完成”的中間狀態(tài)事務(wù)數(shù)據(jù),;Saga協(xié)調(diào)器實現(xiàn)原子特性,,通過日志實現(xiàn)持久性;通過本地日志與Saga日志保證事務(wù)一致性,。所以Saga模式只支持ACD,,不提供隔離性的保證。 Saga與2PC的區(qū)別 Saga與2PC最主要的區(qū)別在于,,2PC是一個強一致性的分布式事務(wù)模式,,Saga和TCC都通過應(yīng)用服務(wù)層犧牲ACID特性來實現(xiàn)事務(wù)的最終一致性,Saga和TCC可以理解為分布式環(huán)境下通過事務(wù)補償模式的事務(wù)控制模型,,只是Saga和TCC有各自不同的實現(xiàn)策略,。 Saga與TCC的區(qū)別 Saga和TCC最大的區(qū)別在于,Saga沒有預(yù)留資源,,而是直接提交到數(shù)據(jù)庫,,Saga比TCC少了一步Try操作,無論最終事務(wù)成功或失敗,,TCC都需要與事務(wù)參與方交互兩次,。而Saga在事務(wù)成功的情況下只需要與事務(wù)參與方交互一次。如果事務(wù)失敗,,則需要采取補償事務(wù)的方式進行回滾,。 Saga的開源解決方案 Saga為相互獨立、自治的微服務(wù)提供了一種分布式網(wǎng)絡(luò)場景下的數(shù)據(jù)一致性的解決方案。下面是一些Saga模式的開源解決方案,,由于篇幅所限,,這里不再贅述方案的實現(xiàn)細(xì)節(jié)。 ● ServiceComb Saga ● Axon Framework ● Eventuate-tram-sagas 可靠消息模式可靠消息模式主要采用一個可靠的消息中間件作為中介,,事務(wù)的發(fā)起方在完成本地事務(wù)后向可靠的消息中間件發(fā)起消息,,事務(wù)消費方在收到消息后處理消息,該方案強調(diào)的是雙方最終的數(shù)據(jù)一致性,。 如下圖所示,,訂單服務(wù)將消息發(fā)送給訂單服務(wù)隊列,庫存服務(wù)監(jiān)聽訂閱了訂單服務(wù)的消息隊列,,并從消息隊列中消費信息,。由此可以看到,從事務(wù)的發(fā)起方到消息中間件,,再到事務(wù)的消費方,,中間都會通過網(wǎng)絡(luò),由于網(wǎng)絡(luò)的不可靠性,,導(dǎo)致分布式事務(wù)的數(shù)據(jù)不一致性,。 基于這種網(wǎng)絡(luò)的不可靠性,依靠本地消息表和可靠消息隊列的模式可以解決分布式事務(wù)的不一致性,。而這種模式,,對業(yè)務(wù)的侵入性相對較小,,在實現(xiàn)復(fù)雜度上也比較可控,,國內(nèi)很多互聯(lián)網(wǎng)公司都采用了可靠消息模式來解決分布式事務(wù)的一致性問題。而這一方案的提出和思路來源于Ebay,,之后這種模式在業(yè)內(nèi)被廣泛使用,。這種分布式事務(wù)模式的本質(zhì)是本地事務(wù)+可靠消息,以達到最終事務(wù)的一致性,。我們來看可靠消息模式的具體實現(xiàn)原理,,如下圖所示。 可靠消息模式的思想是:事務(wù)的發(fā)起方需要額外創(chuàng)建一個本地消息表,,將本地消息表和業(yè)務(wù)數(shù)據(jù)放在同一個事務(wù)中提交執(zhí)行,。也就是說,二者要么同時成功,,要么同時失敗,。例如,將訂單創(chuàng)建事務(wù)和庫存驗證及庫存出庫事件消息日志放入同一個事務(wù)中,,下面是偽代碼舉例: 從上述偽代碼可知,,本地數(shù)據(jù)庫與庫存出庫事務(wù)處于同一事務(wù)中,二者的綁定操作具備了原子性,工作時序如下: (1)本地事務(wù)提交后,,可以使用觸發(fā)的方式對本地消息表進行查詢和消息推送,,或者使用定時器的方式輪詢本地消息表進行消息推送。 (2)消費者的消息可以使用可靠的消息中間件機制,,例如RabbitMQ中的ACK(消息確認(rèn)機制)保證消費者可以一定消費到消息,; 又如庫存服務(wù)在接收到消息并且完成消息業(yè)務(wù)處理后,回復(fù)ACK,,說明此時庫存服務(wù)已經(jīng)正確同步數(shù)據(jù),;如果庫存服務(wù)沒有回復(fù)ACK,則消息中間件在沒收到ACK消息時,,將保留消息,,并重復(fù)投遞此消息。 (3)當(dāng)消息中間件反饋消費成功后,,庫存服務(wù)可以回調(diào)一個訂單服務(wù)的確認(rèn)API,,這時訂單服務(wù)可以從本地事務(wù)表中刪除對應(yīng)的消息隊列。 (4)在訂單服務(wù)中,,如果定時任務(wù)重復(fù)把本地事務(wù)表中的消息發(fā)到庫存服務(wù),,則需要消息消費方(庫存服務(wù))提供消息的冪等性支持。 冪等性 簡單來說,,冪等性的概念就是:除了錯誤或者過期的請求(換言之就是成功的請求),,無論多次調(diào)用還是單次調(diào)用,最終得到的效果都是一致的,。通俗來說,,只要有一次調(diào)用成功,再采用相同的請求參數(shù),,無論調(diào)用多少次(重復(fù)提交),,都應(yīng)該返回成功。 例如庫存服務(wù)對外提供服務(wù)接口,,必須承諾實現(xiàn)接口的冪等性,,這一點在分布式系統(tǒng)中極其重要。 ● 對于HTTP調(diào)用,,承諾冪等性可以避免表單或者請求操作重復(fù)提交,,造成業(yè)務(wù)數(shù)據(jù)重復(fù)。 ● 對于異步消息調(diào)用,,承諾冪等性通過對消息去重處理也是為了避免重復(fù)消費造成業(yè)務(wù)數(shù)據(jù)重復(fù),。 下面是幾種常用的冪等性處理設(shè)計方案。 ● 數(shù)據(jù)庫表設(shè)計對邏輯上唯一的業(yè)務(wù)鍵唯一索引,,這是在數(shù)據(jù)庫層面做最后的保障,。 ● 業(yè)務(wù)邏輯上的防重,,例如創(chuàng)建訂單的接口,首先通過訂單號查詢庫表中是否已經(jīng)存在對應(yīng)的訂單,,如果存在,,則不做處理,直接返回成功,。 補償方案 在可靠消息事務(wù)方案中,,事務(wù)發(fā)起方需要確保消息發(fā)送到消息隊列中,而消息隊列成為系統(tǒng)的瓶頸,。當(dāng)消息隊列異常,,或者消息消費失敗導(dǎo)致數(shù)據(jù)不一致時,需要采取補償措施,,而常用的補償方案由消息消費方負(fù)責(zé),。之所以使用消費方補償模式有下面兩個主要理由: ● 一般來說,數(shù)據(jù)不一致大概率發(fā)生在消息消費異常場景,,如果由消息發(fā)送方補償錯誤,,往往無法解決消費異常問題;同時一個消息可能存在多個消費方,,如果消息補償模塊在所有上游服務(wù)中編寫,,可能無法滿足所有消費方的異常補償場景。 ● 當(dāng)消費方出現(xiàn)問題時,,需要定位事務(wù)發(fā)起方,,然后才能通過上游來補償,這種方式會增加處理生產(chǎn)問題的復(fù)雜度,。 異步消息交互時,,采取的補償措施通常統(tǒng)一由消息消費方實現(xiàn),這種方式將類似本地事件表的方式,,在消息消費失敗后,,將失敗消息寫入本地數(shù)據(jù)庫,,然后啟動定時任務(wù)進行重試,,當(dāng)達到重試上限時,進行預(yù)警和人工干預(yù),。 RabbitMQ可靠消息傳輸實踐 在眾多消息隊列中,,RabbitMQ最重要的特性就是將消息的可靠性作為傳輸消息考慮的第一要素。目前,,RabbitMQ已經(jīng)成為金融行業(yè)中消息隊列的標(biāo)配,。我們將通過RabbitMQ的幾個關(guān)鍵因素講解RabbitMQ如何保證消息的可靠傳輸。RabbitMQ流程如下圖所示,。 確認(rèn)機制 網(wǎng)絡(luò)異常,、機器異常、程序異常等多種情況都可能導(dǎo)致業(yè)務(wù)丟失消息。對消息進行確認(rèn)可以解決消息的丟失問題,,確認(rèn)成功意味著消息已被驗證并被正確處理,。確認(rèn)機制能用在兩個方向:允許消費者告訴服務(wù)器(Broker)已經(jīng)收到了消息,也允許服務(wù)器告訴生產(chǎn)者接收到了消息,。前者就是我們常說的消費者ACK,,后者就是我們常說的生產(chǎn)者Confirm。 RabbitMQ使用生產(chǎn)者消息確認(rèn),、消費者消息確認(rèn)機制來提供可靠交付功能,。 ● 生產(chǎn)者消息確認(rèn):生產(chǎn)者向RabbitMQ發(fā)送消息后,等待它回復(fù)確認(rèn)成功,;否則生產(chǎn)者向RabbitMQ重發(fā)該消息,。此過程可以異步進行,生產(chǎn)者持續(xù)發(fā)送消息,,RabbitMQ將消息批量處理后再回復(fù)確認(rèn),;生產(chǎn)者通過識別確認(rèn)返回中的ID來確定哪些消息被成功處理。開啟生產(chǎn)者消息確認(rèn)機制: ● 消費者消息確認(rèn):RabbitMQ向消費者投遞消息后,,等待消費者回復(fù)確認(rèn)成功,;否則RabbitMQ重新向消費者投遞該消息。該過程同樣可以異步處理,,RabbitMQ持續(xù)投遞消息,,消費者批量處理完后回復(fù)確認(rèn)??梢钥闯?,RabbitMQ/AMQP提供的是“至少一次交付”(at-least-once delivery)的策略,異常情況下消息會被重復(fù)投遞或消費,。開啟消費者消息確認(rèn)機制: 持久化機制 設(shè)置交換機,、隊列和消息都為持久化,它可以在服務(wù)器重啟時保證消息不丟失信息,,集群節(jié)點提供冗余能力,,對解決Broker的單點故障至關(guān)重要。在RabbitMQ集群中,,所有的定義都可以被冗余處理,,例如交換器和綁定關(guān)系等,而隊列只存在于一個節(jié)點上,。對于隊列而言,,可以通過配置把隊列鏡像到多個節(jié)點上。 ● 交換機的持久化(通過查看源碼易知,,默認(rèn)是支持持久化的),,代碼如下: ● 隊列的持久化(通過查看源碼易知,,默認(rèn)是支持持久化的),代碼如下: ● 消 息 的 持 久 化 ,。 當(dāng) 我 們 使 用 RabbitTemplate 調(diào) 用convertAndSend(String exchange,,String routingKey,final Object object)方法時,,默認(rèn)是持久化模式,。 生產(chǎn)者 當(dāng)使用確認(rèn)機制時,生產(chǎn)者從連接或者Channel故障中恢復(fù)過來,,會重發(fā)沒有被Broker確認(rèn)簽收的消息,。如此一來,消息就可能被重復(fù)發(fā)送,,可能是由于網(wǎng)絡(luò)故障等原因,,Broker發(fā)送了確認(rèn),但是生產(chǎn)者沒有收到而已,?;蛘呦⒏揪蜎]有發(fā)送到Broker。正因為生產(chǎn)者為了可靠性可能會重發(fā)消息,,所以在消費者消費消息處理業(yè)務(wù)時,,還需要去重,或者對接收的消息做冪等處理(推薦冪等處理),。生產(chǎn)者增加確認(rèn)機制非常簡單,,Channel開啟Confirm模式,然后增加監(jiān)聽即可,。 說 明 : RabbitMQ 還 有 事 務(wù) 機 制 ( txSelect ,、 txCommit 、txRollback),,也能保障消息的發(fā)送,。不過事務(wù)機制是“同步阻塞”的,所以不推薦使用,。而Confirm模式是“異步”機制,。通過事務(wù)機制與Confirm模式的TPS性能對比,我們可以很明顯地看到,,事務(wù)機制是性能最差的,。 RabbitMQ支持的4種交換器類型中,只有fanout模式不存在路由不到隊列的情況,。因為它會自動路由到所有隊列中,跟綁定Key沒有任何關(guān)系,。所以,,在滿足業(yè)務(wù)的前提下,,筆者建議,盡可能使用fanout模式的類型交換器,。 DLX(Dead Letter Exchange,,死信郵箱或死信交換機)就是一個普通的交換機,與一般的交換機沒有任何區(qū)別,。當(dāng)消息在一個隊列中變成死信時,,通過這個交換機將死信發(fā)送到死信隊列中。 我們可以通過DLX來解決這個問題,,假設(shè)一些消息沒有被消費,,那么它就會被轉(zhuǎn)移到綁定的DLX上。對于這類消息,,我們消費并處理死信隊列即可,。 消費者 只有消費者確認(rèn)的消息,RabbitMQ才會刪除它,,不確認(rèn)就不會被刪除,。所以,在消費端,,建議關(guān)閉自動確認(rèn)機制,。應(yīng)該在收到消息、處理完業(yè)務(wù)后,,手動確認(rèn)消息,。消費者手動確認(rèn)消息的實現(xiàn)代碼如下: 注意上面方法中void basicAck(long deliveryTag,booleanmultiple)的第二個參數(shù)multiple,。要說明這個參數(shù)的含義,,首先需要清楚“deliveryTag”概念,即投遞消息的唯一標(biāo)識符,,它是一個“單調(diào)遞增”的Long型正整數(shù),。假設(shè)此次basicAck的tag為123456,如果 multiple=false ,, 則 表 示 只 確 認(rèn) 簽 收 這 一 條 消 息 ,。 如 果multiple=true,則表示確認(rèn)簽收tag小于或等于123456的所有消息,。 小結(jié)在微服務(wù)架構(gòu)下,,我們強調(diào)要根據(jù)微服務(wù)的數(shù)據(jù)類型和業(yè)務(wù)場景選擇合適的后端數(shù)據(jù)存儲類型。對于微服務(wù)架構(gòu)下分布式應(yīng)用中的數(shù)據(jù)一致性管理,,不推薦使用分布式事務(wù),,微服務(wù)數(shù)據(jù)架構(gòu)通過放棄分布式網(wǎng)絡(luò)的強一致性,來提升微服務(wù)之間的交互性能,。另外,,在微服務(wù)數(shù)據(jù)架構(gòu)中,,我們介紹了常見的TCC、Saga,、可靠消息模式,,可以作為保證數(shù)據(jù)之間最終一致性的解決方案。 |
|
來自: 邸彥強 > 《中臺服務(wù)》