分佈式事務處理方式要點(轉載)

1,柔性事物,二階段2PC型,補償型,異步確保型,最大努力通知型。redis

2PC適合場景:客戶帳,收費spring

異步確保型:會計性,資金訂單,通知數據。sql

核心交易數據分庫並分表,消費記錄數據分庫分表,商戶交易數據分庫分表。數據庫

保持多個維度的數據集羣可使用MQ異步同步,MQ異步也會致使數據不一致,則引入實時監控服務,實時計算2個維度集羣差別,做一致性同步。緩存

2,事務型MQ,在本地邏輯處理前發送MQ,本地邏輯執行成功後,提交commit MQ消息,MQ失敗則重試直到成功。也可用MQ定時補償,單獨補償任務表出來,這樣顯得更爲清晰。服務器

一致性重試須要解決冪等,能夠採用樂觀鎖,version號去重,樂觀鎖失敗證實版本上升了,失敗了走查version再更新,設計較多的去重的能夠單獨一張表作去重服務,利用DB惟一索引特性,或採用zookeeper和redis分佈式鎖lock,相似多線程併發鎖。微信

避免XA事務,通訊時間較長資源鎖定等待久。利用異步作最終一致性方式較好,MQ如支付寶帳號扣錢以後只生成1個MQ便可,只要該消息可靠保存,依靠該消息完成最終一致性。網絡

3,微信支付採用postgreSQL數據庫,XS在事務管理機制會成爲系統瓶頸,全局事務管理器會限制系統的擴展規模,每一個request進來後協調節點都會向全局事務管理器GTM申請必須的全局事務ID,全局快照信息,並把這些信息隨着SQL自己一塊兒發往DN節點執行,只有主DN纔會獲取gxid,備DN沒有本身的gxid。微信改進後再也不從全局事務管理器GTM獲取gxid和全局快照信息,每一個節點使用本身的本地xid和快照,備DN也能夠提供只讀服務,先對分佈式列計算hash,而後用這個值對集羣中的節點個數取模決定哪一個節點。多線程

4,一致性補償服務,須要先要肯定失敗的步驟和狀態,肯定補償的範圍,再能提供補償使用的業務數據和業務流水等更多要素,當客戶1個預約請求到達時,補償協調服務爲請求生成1個全局惟一的業務流水號,並在調用各個工做服務的的同時完整記錄完整的狀態,記錄調用的業務流水,記錄調用bookhotel業務流水和bookTrain流水再調用服務等,補償過程服務一樣也須要重試機制,要求自己也具備冪等性,因爲業務因素失敗不須要重試而是數據庫等記錄異常手動處理,因網絡緣由則須要重試,500錯誤的則等一段時間再重試。架構

5,dubbo分佈式事務,使用異常exception拋出,A服務調用B服務,B服務調用C服務,C服務出現異常會拋給B,B異常也會拋給A,C回滾則B也回滾,B回滾則A也回滾。MQ最終一致性能夠利用系統控制MQ的消息狀態如預備、發送、提交、成功、失敗、重試中等。TCC補償事務則分爲try confiirm cancel三個途徑,try對業務檢測及資源預留confirm作業務提交,cancel作業務回滾,Try節點可能存在失敗則調用cancel釋放資源。 最大努力通知型多用在第三方系統通信時,如調用微信支付後結果通知,如經過MQ發送http請求,設置最大通知數,不保證成功,提供結果查詢接口,多系統之間同步,Dubbo之間調用也可採用最大通知反饋。

6,用戶啓動1個事務,transactionManager.begin(),事務管理器在當前線程初始化1個事務實例,threadLocal.set( new TransationImpl() ),用戶調用jdbc/jms/dubbo請求,請求內部初始化1個XAResource實例,XAResource xsResource = new XAResourceImpl(); jdbc/jms/dubbo從當前線程獲取事務,transation將當前XAResource註冊到事務中,transation.enlistResource(xsResource),用戶提交1個事務:transationManager.commit()。事務for循環調用全部註冊的XAResource的2階段提交,Xid xid = new XidImpl(); for(XAResource x : xaResources){ x.prepare(ID), xa.commit(xid, true); 還有些異常流程如rollback和forget等,距離TransationManager tm = newTransationManager (); JNDI並行lockup方式獲取,tm.begin() 肯定事務, jdbcConn執行update sql,DB寫入binlog,但不更新表,jmsMQ發送消息msg可是不執行提交,dubbo invoke調用遠程服務,provider緩存請求不執行提交,全部提交後捕獲異常rollback。

7,四個dubbo服務,2個失敗則等4個任務全執行完畢後回調,定時任務檢測4個dubbo服務數據是否一致,一致則所有置爲成功,不一致則所有置爲失敗 。

8,發紅包,推送,統計,分訂單,支付,網銀第三方等異步操做使用最終一致性,重試冪等等,如退款遇到故障會延時退款,保證最終會被完成,不過須要判斷退款保持冪等性,若是發生退款失敗則放入延時隊列,稍後重試。退款操做最終必定會完成。

9,不少交易環節採用MQ來流轉,業務操做上先將訂單持久化到同一個事務裏面,共享訂單操做的持久化DB鏈接,在這個DB鏈接中將MQ持久化到同一個實例的消息庫中,而後在事務提交之江消息發送到MQ server,事務回滾後消息不會發出去,訂單持久化,MQ持久化到DB,事務提交MQ發出去,發送成功則刪除DB中的消息表記錄,若是發送失敗,後臺有1個任務會將消息表中發送失敗的消息從新發送,達到最終一致性,或者producer是發或者補發,提供1個broker反查接口。也能夠採用spring等自帶的MQ事務進行延時重試等。冪等能夠採用狀態機,或者攜帶版本號,和DB中的進行對比,後臺掃描制定的時間以內的DB消息表,server段據消息ID作1個去重。

10,A,前者1個嵌入客戶端應用的jar負責事務,數據寫入處理,後者獨立系統,負責異常、事務恢復、2PC需參與者須要一直到持有資源直到整個分佈式事務結束,千萬級,性能不好,1個業務服務若干個從,主發起完成整個業務,從做TCC型業務操做,業務活動管理器控制業務活動一致性,登記業務活動中的操做,在活動提交時確認全部的2階段事務的操做,業務取消時能夠調用全部2階段事務cancel操做。

B,發起支付,增長用戶積分,調用dubbo支付扣款,支付狀態處理,成功則提交事務,失敗則回調訂單積分,未知則等到支付系統回調狀態。

C,用戶信息變動同步至各業務系統activeMQ中,訂閱方式保證單次消費。

D,非實時非強一致性的業務採用MQ,能夠重試和事務消息。

E,事務提交以前插入1條消息記錄,事務提交以後再一步將該消息發出去,成功則將消息記錄刪除,失敗則保留該消息記錄,會有1個服務不斷掃描消息從新發送。

11,跨銀行轉帳,2個異地service服務,構成1個完整事務,事務補償即鏈中每一個正向事務必須有1個徹底符合回滾的逆向事務,調用本地取款,成功並返回數據已持久化,而後調用異地存款,如失敗則調用本地逆向服務,如本地存服務調用失敗則必須重試,約定重試次數仍然失敗則必須log到完整的不一致信息(也能夠MQ),爲了不手工寫大量的代碼,考慮1個通用的事務管理器,實現事務鏈和上下文的管理。任務1個正向和逆向均在事務管理器上註冊,由事務管理器接管全部在事務補償和回滾操做。

消息一致性調用本地取款服務,而後發送MQ,MQ消費對消息解析後調用異步存款服務,失敗則重試,不得已本地取款通常不可逆。(能夠存在真空期時間內事務最終一致性)。冪等性重試重複調用屢次產生的業務結果和1次調用同樣,由於調用失敗異常必須考慮。

12,一致性結果可能成功,失敗或者不肯定,成功就能夠把記錄的東西清理掉,對於失敗和不肯定,能夠依靠定時方式把全部失敗的事情從新嘗試一遍,直到成功爲止。系統在A扣錢成功下,把要給B通知這件事記錄到數據庫中,爲了保證可靠性能夠把通知B系統加錢和扣錢成功這2件事維護在本地1個事務裏面,本地事務維護業務變化和通知消息一塊兒落地(失敗則一塊兒回滾),而後RPC到達broker,在broker成功落地後RPC返回成功,本地消息記錄能夠刪除,不然本地消息記錄一致靠定時器任務輪詢不斷重發,這樣就保證了消息可靠落地broker。broker一致發消息給consumer,直到consumer確認消費成功,先不理會重複消費,以來狀態機作版本號去重,實現最終一致性。

13,順帶記錄下spring的5中事務方式,每一個bean都有1個事務代理;全部bean共享1個事務代理;使用攔截器;使用tx標籤配置的攔截器;全註解方式。

14,建立不可見訂單,同時鎖券,失敗則發送廢棄訂單消息到MQ,而後回庫存和回券,鎖券成功則訂單變爲可見。支付系統回調請求會重試到MQ,交易系統會更新交易主子菜單,交付單,減庫存,加銷量,送券等,若是業務失敗則放入重試表,做重試。其中資金做爲發起方保證重試和MQ可達。

15,最終一致性,T1發送half消息,T2存儲half消息,T3返回消息入庫結果,T3業務操做,S1定時檢查未提交的消息,S2提交回滾,S3提交更新DB/回滾刪除消息。

16,XA事務:1個數據庫只將其操做(可恢復)映射到全局事務中(交易中間件),由它通知協調相關數據庫的提交和回滾

2PC(二階段):參與者將本身操做成敗通知協調者,再由協調者根據全部參與者反饋狀況情報決定參與者是否須要提交操做仍是終止。2階段爲準備階段和提交階段。分爲三個步驟,A協調者節點向全部參與者詢問是否可執行提交,並等待全部參與者響應 B參與者節點詢問發起爲止全部事務操做,並將undo和redo寫入日誌 C各參與者節點響應協調者發起的詢問,若是參與者節點事務操做實際執行成功,則返回1個贊成,參與者操做失敗則返回1個終止。

17,Sagas長事務

在Sagas事務模型中,一個長事務是由一個預先定義好執行順序的子事務集合和他們對應的補償子事務集合組成的。典型的一個完整的交易由T一、T二、……、Tn等多個業務活動組成,每一個業務活動能夠是本地操做、或者是遠程操做,全部的業務活動在Sagas事務下要麼所有成功,要麼所有回滾,不存在中間狀態。

Sagas事務模型的實現機制:

每一個業務活動都是一個原子操做;

每一個業務活動均提供正反操做;

任何一個業務活動發生錯誤,按照執行的反順序,實時執行反操做,進行事務回滾;

回滾失敗狀況下,須要記錄待衝正事務日誌,經過重試策略進行重試;

衝正重試依然失敗的場景,提供定時衝正服務器,對回滾失敗的業務進行定時衝正;

定時衝正依然失敗的業務,等待人工干預;

Sagas長事務模型支持對數據一致性要求比較高的場景比較適用,因爲採用了補償的機制,每一個原子操做都是先執行任務,避免了長時間的資源鎖定,能作到實時釋放資源,性能相對有保障。

Sagas長事務方式若是由業務去實現,複雜度與難度並存。在咱們實際使用過程當中,開發了一套支持Sagas事務模型的框架來支撐業務快速交付。

開發人員:業務只須要進行交易編排,每一個原子操做提供正反交易;

配置人員:能夠針對異常類型設定事務回滾策略(哪些異常歸入事務管理、哪些異常不歸入事務管理);每一個原子操做的流水是否持久化(爲了避免同性能能夠支持緩存、DB、以及擴展其它持久化方式);以及衝正選項配置(重試次數、超時時間、是否實時衝正、定時衝正等);

Sagas事務框架:提供事務保障機制,負責原子操做的流水落地,原子操做的執行順序,提供實時衝正、定時衝正、事務攔截器等基礎能力;

Sagas框架的核心是IBusinessActivity、IAtomicAction。IBusinessActivity完成原子活動的enlist()、delist()、prepare()、commit()、rollback()等操做;IAtomicAction主要完成對狀態上下文、正反操做執行。

限於文章篇幅,本文不對具體實現作詳述;後面找時間能夠詳細介紹基於Sagas長事務模型具體的實現框架。Sagas長事務須要交易提供反操做,支持事務的強一致性,因爲沒有在整個事務週期內鎖定資源,對性能影響較小,適合對數據要求比較高的場景中使用。

18,可靠事件模式(本地事件表、外地事件表)

可靠事件模式屬於事件驅動架構,當某件重要事情發生時,例如更新一個業務實體,微服務會向消息代理髮佈一個事件。消息代理會向訂閱事件的微服務推送事件,當訂閱這些事件的微服務接收此事件時,就能夠完成本身的業務,也可能會引起更多的事件發佈。

可靠事件模式在於保證可靠事件投遞和避免重複消費,靠事件投遞定義爲:

每一個服務原子性的業務操做和發佈事件;

消息代理確保事件傳遞至少一次;避免重複消費要求服務實現冪等性。

基於事件模式,須要重點考慮的是事件的可靠到達,在咱們產品實際支持過程當中,一般有本地事件表、外部事件表兩種模式:

一、本地事件表方法將事件和業務數據保存在同一個數據庫中,使用一個額外的「事件恢復」服務來恢復事件,由本地事務保證更新業務和發佈事件的原子性。考慮到事件恢復可能會有必定的延時,服務在完成本地事務後可當即向消息代理髮佈一個事件。

微服務在同一個本地事務中記錄業務數據和事件;

微服務實時發佈一個事件當即通知關聯的業務服務,若是事件發佈成功當即刪除記錄的事件;

事件恢復服務定時從事件表中恢復未發佈成功的事件,從新發布,從新發布成功才刪除記錄的事件;

其中第2條的操做主要是爲了增長髮布事件的實時性,由第三條保證事件必定被髮布。本地事件表方式業務系統和事件系統耦合比較緊密,額外的事件數據庫操做也會給數據庫帶來額外的壓力,可能成爲瓶頸。

二、外部事件表方法將事件持久化到外部的事件系統,事件系統需提供實時事件服務以接受微服務發佈事件,同時事件系統還須要提供事件恢復服務來確認和恢復事件。

業務服務在事務提交前,經過實時事件服務向事件系統請求發送事件,事件系統只記錄事件並不真正發送;

業務服務在提交後,經過實時事件服務向事件系統確認發送,事件獲得確認後,事件系統才真正發佈事件到消息代理;

業務服務在業務回滾時,經過實時事件向事件系統取消事件;

若是業務服務在發送確認或取消以前中止服務了怎麼辦呢?事件系統的事件恢復服務會按期找到未確認發送的事件向業務服務查詢狀態,根據業務服務返回的狀態決定事件是要發佈仍是取消;

該方式將業務系統和事件系統獨立解耦,均可以獨立伸縮。可是這種方式須要一次額外的發送操做,而且須要發佈者提供額外的查詢接口。

基於可靠事件的事務保障模式能夠有不少的變種實現,好比對消息可靠性不高的話,能夠將本地表的方式換作緩存方式。爲了提升消息投遞的效率,能夠將屢次消息合併爲投遞模式。爲了提供強一致性的事務保障,甚至能夠將本地消息表持久化(保障發方法消息可靠落地)+遠程消息表持久化(保障接收方消息可靠落地)結合的模式。

在咱們的流程產品中針對業務和流程的分佈式事務解決方案就採用了屢次消息合併投遞+本地緩存+遠程消息表持久化的模式,接下來爲你們介紹具體的使用方式。

使用場景

在實際業務項目中一般採用業務與流程分佈式部署的模式,業務系統經過遠程接口訪問流程引擎,業務數據同流程數據存放到各自的數據庫中。

在這種場景中,若是業務系統的流程操做和業務操做交叉在一塊兒,當流程操做成功,而業務操做失敗時,就會形成業務回滾,而流程在引擎端已經建立,致使業務系統和流程引擎狀態不一致。

在業務應用中對一個事務中的流程操做採用本地緩存+批量投遞+遠程落地的模式(若是須要在客戶端確保消息可靠性,能夠將本地緩存換成本地表的方式);在流程引擎端在消息投遞來以後,作了消息表落地的工做,保障可靠執行。在咱們流程產品中流程引擎對外提供的客戶端提供了統一的分佈式事務API,和使用傳統本地事務同樣進行操做,保證了透明性,簡化開發人員的複雜度。分佈式事務API支持兩種協議模式:

HTTP + 二進制序列化的模式

WebService模式

後續咱們會增長Restful風格的接口。

可靠事件模式在互聯網公司中有着較大規模的應用,該方式適合的業務場景很是普遍,並且可以作到數據的最終一致性,缺點是該模式實現難度較大,依賴數據庫實現可靠性,在高併發場景下可能存在性能瓶頸,須要在公司層面搭建一套標準的可靠事件框架來支撐。

可靠事件模式(非事務消息、事務消息)

可靠事件模式的事件通知能夠採用消息的模式來實現,其實現原理和本地事件表、外部事件表一致,本文就不在詳述。目前經常使用的消息框架ActiveMQ、RabbitMQ、Kafka、RocketMQ能夠用來做爲消息投遞的渠道。注意:Kafka一般不適合,由於Kafka的設計存在丟消息的場景。

目前市面上支持事務的消息產品比較少,RocketMQ雖然實現了可靠的事務模式,但並無開源出來、沒有開源出來、沒有開源出來,順便說一下國內的開源有太多須要改進的空間(關鍵點不開源,開源後沒有持續的投入)。

 

相關文章
相關標籤/搜索