文章摘要:原來大型分布式/微服務系統(tǒng)中解決數(shù)據(jù)一致性問題,居然是通過……
一溉潭、為什么要使用分布式事務—2PC净响?
傳統(tǒng)事務是使用數(shù)據(jù)庫自身的事務屬性(ACID),而數(shù)據(jù)庫自身的事務屬性是局限于當前實例喳瓣,不能實現(xiàn)跨庫馋贤。而對于大型分布式/微服務集群系統(tǒng)中,不僅存在著跨庫的事務畏陕,還存在很多不同系統(tǒng)/服務之間的RPC調用配乓,這種調用往往也需要保證業(yè)務以及數(shù)據(jù)的一致性。因此蹭秋,有必要使用一種分布式事務框架來協(xié)調整個端到端業(yè)務調用鏈路的應用和數(shù)據(jù)庫來保證業(yè)務最終的數(shù)據(jù)一致性扰付,而目前在分布式事務中用的比較多的即為基于所有服務參與者投票的二階段協(xié)議(2PC)。
二仁讨、分布式事務基礎模型
由于在大型SOA/微服務系統(tǒng)架構下羽莺,一次業(yè)務請求往往會跨越多個服務,多個服務共同協(xié)調完成一次端到端的全鏈路業(yè)務調用時洞豁,由于業(yè)務約束或者服務提供者的故障等原因造成多個系統(tǒng)中數(shù)據(jù)不一致或通信延遲等問題盐固,因此對于分布式/微服務系統(tǒng)中的多服務協(xié)調調用場景需要分布式事務來保證系統(tǒng)的ACID屬性。
1.X/Open DTP模型
目前二階段的分布式事務一般基于以下的X/Open
DTP(Distributed Transaction Processing Reference Model)丈挟。
其中刁卜,
AppService:是服務應用∈镅剩可以理解為使用分布式事務的應用程序蛔趴。
RM(Resource Manager):資源管理器。這里主要指的是服務應用通過資源管理器對數(shù)據(jù)庫或者消息隊列服務器這類資源進行控制例朱,資源需要實現(xiàn)XA接口孝情;
TM(Transaction Manager):事務管理器。負責協(xié)調和管理事務洒嗤,提供給服務應用編程接口管理上面的資源管理器箫荡。
AppService可以和TM、RM進行通信渔隶,TM和RM也能夠進行通信羔挡,服務應用可以通過TX接口向事務管理器發(fā)起事務、提交事務和回滾事務间唉。TM和RM通過XA接口進行雙向通信绞灼,比如TM通知RM提交事務或回滾事務;RM把提交結果通知給TM呈野。AppService和RM之間則通過RM提供的本地API接口進行資源控制镀赌。
2.分布式二階段提交協(xié)議
通常在一個全鏈路的端到端業(yè)務操作中,往往會跨多個節(jié)點际跪、多個應用商佛,為了能夠保證全局事務的ACID特性喉钢,需要引入一個協(xié)調組件(這里為TM)來控制所有服務參與者(這里為RM)的操作結果,根據(jù)所有參與者的反饋結果來決定整個分布式事務究竟是提交還是回滾的結果良姆。
第一階段:稱為準備(prepare)階段肠虽。事務協(xié)調者向各個服務應用發(fā)送prepare請求,服務應用在得到請求后做預處理操作玛追,預處理可能是做預檢查税课,也可能是把請求臨時存儲,可以理解為是一種試探性地提交痊剖。下面是一般的步驟:
a.事務協(xié)調者會問所有的參與者服務韩玩,是否可以提交操作。
b.各個參與者開始事務執(zhí)行的準備工作:如資源上鎖陆馁,預留資源找颓,寫回滾/重試的log。
c.參與者響應協(xié)調者叮贩,如果事務準備工作成功击狮,則回應“可以提交”,否則回應拒絕提交益老。
第二階段:稱為提交(commit)/回滾(rollback)階段彪蓬。是指事務真正提交或者回滾的階段。如果事務協(xié)調者發(fā)現(xiàn)事務參與者有一個在prepare階段出現(xiàn)失敗捺萌,則會要求所有的參與者進行回滾档冬。如果協(xié)調者發(fā)現(xiàn)所有的參與者都prepare操作都是成功,那么他將向所有的參與者發(fā)出提交請求桃纯,這時所有參與者才會正式提交酷誓。由此保證了要求全部提交成功,要么全部失敗慈参。下面是具體步驟:
a.如果所有的參與者都回應“可以提交”呛牲,那么協(xié)調者向所有參與者發(fā)送“正式提交”的命令刮萌。參與者完成正式提交驮配,并釋放所有資源,然后回應“完成”着茸,協(xié)調者收集各個服務的“完成”回應后結束事務壮锻。
b.如果有一個參與者回應“拒絕提交”,那么協(xié)調者向所有的參與者發(fā)送“回滾操作”涮阔,并釋放所有的資源猜绣,然后回應“回滾完成”,協(xié)調者收集各個服務應用的“回滾”返回后敬特,取消整體的分布式事務掰邢。
下圖為二階段的成功和失敗示例圖:
三牺陶、分布式事務—2PC的基本概念&&主要執(zhí)行流程
下面先用一張框圖來大致描述下分布式事務DTS,二階段提交是如何執(zhí)行的:
上面的圖中是以訂單支付來舉例的辣之。圖中的業(yè)務核心這里主要指的訂單系統(tǒng)(訂單系統(tǒng)作為大型微服務系統(tǒng)中的一個服務本身并不具備支付的服務能力掰伸,因此需要調用其他或者第三方的賬務計費系統(tǒng)完成支付以及結算業(yè)務),在這里它發(fā)起分布式事務怀估,需要調用賬務系統(tǒng)以完成訂單支付業(yè)務活動狮鸭。DTS協(xié)調者實際上即為DTS Server,它會統(tǒng)一協(xié)調參與分布式事務的各個參與者之間提交/回滾多搀。
1.事務發(fā)起者與參與者
在本文的上篇中歧蕉,只是將分布式系統(tǒng)中所有服務稱之為參與者。由于二階段提交的“準備”操作實現(xiàn)復雜性和效率都會在實際的業(yè)務中產(chǎn)生影響康铭,因此需要引入一個簡單的優(yōu)化惯退,在模型中將事務將有一個參與者不參與二段提交的過程(稱為單階段參與者),而是在其余二階段參與者都準備好后麻削,再請求單階段參與者提交蒸痹,單階段參與者的提交結果將決定整個分布式事務的結果。如果單階段參與者提交成功呛哟,那么協(xié)調者要求其余參與者都提交叠荠,如果提交失敗則協(xié)調者要求其余參與者都事務回滾。這里的單階段參與者也即為下文所述的事務發(fā)起方扫责。
在二階段的分布式事務中榛鼎,事務發(fā)起者又稱為發(fā)起方,它是整個業(yè)務活動的主體鳖孤、是服務的編排者者娱,由它啟動業(yè)務活動并決定業(yè)務活動提交或回滾。簡單的例子如上圖中苏揣,業(yè)務核心可以是訂單中心等黄鳍,它需要調用賬務計費服務完成支付功能,那么此時它是發(fā)起分布式事務活動的發(fā)起方平匈,它命令其他系統(tǒng)協(xié)助完成一次完整的分布式事務框沟。那么參與到這一次分布式事務中來的其他系統(tǒng)就稱為參與者,這里賬務計費服務就是參與者增炭,它協(xié)助發(fā)起方完成相應的動作忍燥,執(zhí)行具體的業(yè)務邏輯。
下面給出兩個定義:
(1)Activity:把整個分布式事務稱為一次主業(yè)務活動(Activity)
(2)Action::把參與者的一次方法調用稱為原子活動(Action)
分布式事務必須確保各個角色的一致性和有效性隙姿,因此要保證框架進行提交或者回滾梅垄,Activity和Action,必須在分布式事務執(zhí)行階段將狀態(tài)等信息記錄下來输玷,如果這些信息只記錄在內存或者本地存儲中队丝,該中記錄方式容錯性低靡馁,無法應付宕機等問題,因此這些狀態(tài)信息記錄在DB中机久,通過建立分布式業(yè)務控制活動主表(business_activity)來記錄全局事務的活動狀態(tài)奈嘿,以及原子業(yè)務活動表(business_action)來記錄原子業(yè)務活動的狀態(tài)。在開始一個分布式事務的時候框架先創(chuàng)建activity主活動記錄吞加,每調用一個參與者就會再創(chuàng)建一個action原子活動記錄裙犹。
2.分布式事務中的同庫/異庫模式
這里可以根據(jù)業(yè)務控制活動主表記錄(簡稱為activity)和原子業(yè)務活動表(簡稱為action)記錄是否保存在業(yè)務庫還是中DTS
Server庫,分為以下兩種情況:
(1)同庫模式
發(fā)起方會在自己業(yè)務庫的事務模板(transactionTemplate)中開起分布式事務衔憨,并且將上述的business_activity/business_action存儲在業(yè)務庫中叶圃,由于DB的插入操作相對較快且無需額外的RPC調用,因此適合業(yè)務量比較大的業(yè)務場景践图。但需要進行額外的編程配置掺冠,比如配置數(shù)據(jù)源、事務模板码党、DAO等德崭。
(2)異庫模式
發(fā)起方會在自己業(yè)務庫的事務模板中開起分布式事務,而business_activity/business_activity這兩個表會存儲在DTS Server這個中間協(xié)調者的數(shù)據(jù)庫內揖盘,即為這兩個表和業(yè)務表不同庫眉厨。該模式下,通常需要RPC調用DTS Server查詢數(shù)據(jù)庫的元數(shù)據(jù)兽狭。適用業(yè)務量較小的場景憾股。但是分布式活動方便集中管理,管理方便易于排查問題箕慧。
同庫模式和異庫模式存在的原因是必須記錄主事務的信息到DB中服球,方便提交/回滾以及recover(如果是同庫模式,主要是recovery颠焦,如果是異庫模式就是提交/回滾+recovery),確保各系統(tǒng)之間的一致性斩熊,但是由于分布式事務中必然存在一些外圍系統(tǒng)接入而且根據(jù)業(yè)務情況無法保證數(shù)據(jù)表總是存放在本地庫中,因此產(chǎn)生了該兩種模式伐庭。
同樣對于整個調用鏈路中的服務應用參與者來說也可以分為以下兩種情況:
(1)local模式
事務元數(shù)據(jù)Action記錄在本地和Activity同庫粉渠,且沒有直接remote參與者,發(fā)起方在本地對參與者發(fā)起提交/回滾似忧。原子活動記錄Action和Activity都是同庫渣叛,速度比較快丈秩。但是盯捌,原子活動的context內容由框架獲取預處理階段提交的參數(shù)無法修改。
(2)remote模式
沒有配置本地數(shù)據(jù)源蘑秽,Activity和Action的記錄都存在DTSServer中饺著,整個回滾和提交過程將由中間協(xié)調者DTS Server來完成箫攀。該種模式的優(yōu)點在于可以自行創(chuàng)建原子活動記錄,能夠控制context內容幼衰;可以啟動嵌套的分布式事務靴跛。然而缺點也比較明顯,創(chuàng)建原子活動記錄需要手動觸發(fā)渡嚣,記錄由遠程服務存儲梢睛,效率比較低。
由于在大型分布式/微服務應用中识椰,實際生產(chǎn)環(huán)境中的業(yè)務量往往較大绝葡,綜合考慮性能、穩(wěn)定性等因素腹鹉,因此通常會使用發(fā)起方同庫+服務參與者local/remote模式藏畅。
3.發(fā)起方和參與者的具體執(zhí)行流程
限于篇幅,下面分別通過兩幅框圖來說明發(fā)起方同庫模式下功咒,服務參與方存在local和remote模式的業(yè)務調用場景愉阎。
(1)一階段:在訂單中心(業(yè)務核心)這個分布式事務發(fā)起方開啟事務模塊,將Activity記錄插入至本地業(yè)務庫的分布式業(yè)務控制活動主表(business_activity)中力奋,對參與者——賬務計費系統(tǒng)發(fā)起事務時榜旦,如果參與者服務響應OK,則攔截器將自動將Action記錄插入至本地的原子業(yè)務活動表(business_action)中景殷。
(2)二階段:在正常的情況下章办,發(fā)起方直接向參與者提交/回滾操作。
(1)一階段:在該模式下滨彻,Activity記錄在發(fā)起方本地藕届,此時參與者服務是remote模式,需要參與者自己向DTS Server注冊分支事務(即為在DTS Server的庫中插入action記錄)亭饵。
(2)二階段:發(fā)起方事務先向DTS Server發(fā)送提交/回滾操作休偶,然后DTS Server再向參與者發(fā)起提交/回滾操作。
4.分布式事務的異常處理流程
任何系統(tǒng)設計都需要考慮服務故障辜羊、服務器宕機踏兜、網(wǎng)絡通信延遲以及數(shù)據(jù)庫不可用等外部因素。上面只是對分布式事務正常的執(zhí)行流程和原理進行了闡述八秃,那如果在事務執(zhí)行中碱妆,一階段失敗回滾的時候,恰好系統(tǒng)服務故障導致回滾不成功昔驱;或者二階段提交時候疹尾,系統(tǒng)服務故障導致提交不成功,那么如何來保證一致性呢?在這里我們可以考慮在分布式事務協(xié)調者的DTS Server中設計一套異衬杀荆恢復機制來對進行容錯和恢復窍蓝。
可以清楚看到,如果狀態(tài)為U繁成,表明有可能是異庫模式下的主活動記錄吓笙,這時并不清楚當前事務到底執(zhí)行到何種狀態(tài),是否是確認提交或者確認回滾巾腕,那么需要對進行回查處理面睛,需要我們自己來寫回查代碼,將回查的結果返回尊搬。如果是DONE侮穿,表明一階段完成,此時對應的是確認提交狀態(tài)亲茅,應為C;如果為NOT_DONE克锣,那么表明一階段失敗,對應了確認回滾狀態(tài)腔长,應為I。然后再調用對應的二階段的提交/回滾就即可捞附。如果撈取出來狀態(tài)為I/C,就直接進行回滾/提交鸟召。
DTS Server中有一個異车ò恚恢復的recover線程在運行欧募,每隔1分鐘就去Activity記錄所在的DB撈取記錄,如果這是狀態(tài)被記錄為異常狀態(tài),那么recover程序就會嘗試恢復被中斷的事務,也就是重試,直到二階段事務成功提交/回滾事務娱两。