微服務(wù)架構(gòu)下的分布式事務(wù)解決方案
隨著業(yè)務(wù)的快速發(fā)展、業(yè)務(wù)復(fù)雜度越來越高,傳統(tǒng)單體應(yīng)用逐漸暴露出了一些問題,例如開發(fā)效率低、可維護(hù)性差、架構(gòu)擴(kuò)展性差、部署不靈活、健壯性差等等。
微服務(wù)架構(gòu)是一個分布式的系統(tǒng),按業(yè)務(wù)進(jìn)行劃分為獨(dú)立的服務(wù)單元,解決單體系統(tǒng)的不足,同時也滿足越來越復(fù)雜的業(yè)務(wù)需求。每個微服務(wù)僅關(guān)注于完成一件任務(wù)并很好地完成該任務(wù)。
微服務(wù)架構(gòu)的特點(diǎn)
微服務(wù)架構(gòu)的優(yōu)勢非常明顯,在近些年迅猛發(fā)展。
- 將復(fù)雜的業(yè)務(wù)拆分成多個小的業(yè)務(wù),能夠達(dá)到更好的業(yè)務(wù)復(fù)用,有利于人員組織分工
- 服務(wù)獨(dú)立部署,獨(dú)立擴(kuò)容,每個服務(wù)的修改和部署對其他服務(wù)沒有影響
- 每個服務(wù)可以根據(jù)業(yè)務(wù)場景選取合適的編程語言和數(shù)據(jù)庫
微服務(wù)有以上的優(yōu)勢,但是微服務(wù)也帶來不少的新問題,例如:
- 服務(wù)數(shù)量眾多,其測試、部署、監(jiān)控等都變的更加困難。
- 單體應(yīng)用拆分為分布式系統(tǒng)后,進(jìn)程間的通訊機(jī)制和故障處理措施變的更加復(fù)雜
- 系統(tǒng)微服務(wù)化后,原先是一個服務(wù)內(nèi)部的本地數(shù)據(jù)庫事務(wù),被拆到了多個服務(wù),需要在分布式環(huán)境下保證事務(wù)的一致性
上述的各項問題中,1、2都可以通過近幾年涌現(xiàn)的各項微服務(wù)技術(shù)解決,例如Kubernetes提供了服務(wù)發(fā)現(xiàn)、服務(wù)治理等。因此分布式事務(wù)已經(jīng)成為微服務(wù)落地最大的阻礙,也是最具挑戰(zhàn)性的一個技術(shù)難題。下面將深入和大家探討微服務(wù)架構(gòu)下,分布式事務(wù)的解決方案。
從本地事務(wù)到分布式事務(wù)的演變
我們那轉(zhuǎn)賬作為例子,A需要轉(zhuǎn)100元給B,那么需要給A的余額-100元,給B的余額+100元,單體模式下,可以通過本地事務(wù)解決。
本地事務(wù)
把多條語句作為一個整體進(jìn)行操作的功能,被稱為數(shù)據(jù)庫_事務(wù)_。數(shù)據(jù)庫事務(wù)可以確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗。如果事務(wù)失敗,那么效果就和沒有執(zhí)行這些SQL一樣,不會對數(shù)據(jù)庫數(shù)據(jù)有任何改動。
數(shù)據(jù)庫事務(wù)具有ACID這4個特性:
- A:Atomic,原子性,將所有SQL作為原子工作單元執(zhí)行,要么全部執(zhí)行,要么全部不執(zhí)行;
- C:Consistent,一致性,事務(wù)完成后,所有數(shù)據(jù)的狀態(tài)都是一致的,即A賬戶只要減去了100,B賬戶則必定加上了100;
- I:Isolation,隔離性,如果有多個事務(wù)并發(fā)執(zhí)行,每個事務(wù)作出的修改必須與其他事務(wù)隔離;
- D:Duration,持久性,即事務(wù)完成后,對數(shù)據(jù)庫數(shù)據(jù)的修改被持久化存儲。
分布式事務(wù)典型場景
銀行跨行轉(zhuǎn)賬業(yè)務(wù)是一個典型分布式事務(wù)場景,假設(shè)A需要跨行轉(zhuǎn)賬給B,那么就涉及兩個銀行的數(shù)據(jù),無法通過一個數(shù)據(jù)庫的本地事務(wù)保證轉(zhuǎn)賬的正確性,只能夠通過分布式事務(wù)來解決。
將服務(wù)拆分為微服務(wù)時,遇見類似需要分布式事務(wù)的場景非常多,雖然微服務(wù)最佳實(shí)踐建議盡量規(guī)避分布式事務(wù),但是在很多業(yè)務(wù)場景,分布式事務(wù)是一個繞不開的技術(shù)問題。
分布式事務(wù)方案
分布式事務(wù)模式常見的有XA、TCC、SAGA、可靠消息,下面進(jìn)行簡短的介紹
兩階段提交/XA
XA是由X/Open組織提出的分布式事務(wù)的規(guī)范,XA規(guī)范主要定義了(全局)事務(wù)管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數(shù)據(jù)庫如MySQL在XA中扮演的是RM角色。
XA一共分為兩階段:
第一階段(prepare):即所有的參與者RM準(zhǔn)備執(zhí)行事務(wù)并鎖住需要的資源。參與者ready時,向TM報告已準(zhǔn)備就緒。
第二階段(commit/rollback):當(dāng)事務(wù)管理者(TM)確認(rèn)所有參與者(RM)都ready后,向所有參與者發(fā)送commit命令。
目前主流的數(shù)據(jù)庫基本都支持XA事務(wù),包括MySQL、Oracle、SQLServer、PostgreSQL。
一個成功完成的XA事務(wù)時序圖如下:
TCC事務(wù)方案
TCC方案其實(shí)是XA提交的一種改進(jìn)。其將整個業(yè)務(wù)邏輯的每個分支顯式的分成了Try、Confirm、Cancel三個操作。Try部分完成業(yè)務(wù)的準(zhǔn)備工作,confirm部分完成業(yè)務(wù)的提交,cancel部分完成事務(wù)的回滾。
事務(wù)開始時,業(yè)務(wù)應(yīng)用會向事務(wù)協(xié)調(diào)器注冊啟動事務(wù)。之后業(yè)務(wù)應(yīng)用會調(diào)用所有服務(wù)的try接口,完成一階段準(zhǔn)備。之后事務(wù)協(xié)調(diào)器會根據(jù)try接口返回情況,決定調(diào)用confirm接口或者cancel接口。如果接口調(diào)用失敗,會進(jìn)行重試。
一個成功完成的TCC事務(wù)時序圖如下:
SAGA事務(wù)方案
Saga和TCC一樣,也是最終一致性事務(wù)、柔性事務(wù)。Saga的本質(zhì)就是把一個長事務(wù)分隔成一個個小的事務(wù),每個事務(wù)都包含一個執(zhí)行模塊和補(bǔ)償模塊。
Saga沒有try,直接提交事務(wù),可能出現(xiàn)臟讀的情況,在某些對一致性要求較高的場景下,是不可接受的。
在啟動一個Saga事務(wù)時,事務(wù)管理器會告訴第一個Saga參與者,也就是子事務(wù),去執(zhí)行本地事務(wù)。事務(wù)完成之后Saga的會按照執(zhí)行順序調(diào)用Saga的下一個參與的子事務(wù)。這個過程會一直持續(xù)到Saga事務(wù)執(zhí)行完畢。
如果在執(zhí)行子事務(wù)的過程中遇到子事務(wù)對應(yīng)的本地事務(wù)失敗,則Saga會按照相反的順序執(zhí)行補(bǔ)償事務(wù)。
一個成功完成的SAGA事務(wù)時序圖如下:
可靠消息
消息一致性方案是通過消息中間件保證上下游應(yīng)用數(shù)據(jù)操作的一致性?;舅悸肥菍⒈镜夭僮骱桶l(fā)送消息放在一個本地事務(wù)中,保證本地操作和消息發(fā)送要么兩者都成功或者都失敗。下游應(yīng)用向消息系統(tǒng)訂閱該消息,收到消息后執(zhí)行相應(yīng)操作。
RocketMQ 提供了典型的可靠消息接口,可以參考。
分布式事務(wù)開源項目
當(dāng)前的分布式事務(wù)領(lǐng)域,有java語言的開源項目,以seata為代表。在非Java領(lǐng)域,Go語言的 DTM 是代表項目。 DTM 支持XA、TCC、SAGA、可靠消息,架構(gòu)圖如下:
圖中的各角色與XA模型中的角色模型一致,分別解釋如下:
- AP應(yīng)用程序(定義和提交事務(wù),當(dāng)前支持Go語言,即將支持Nodejs、Python、PHP、Rust等)
- RM資源管理器(負(fù)責(zé)管理本地事務(wù),不限語言,只要提供了http相關(guān)的接口即可)
- TM事務(wù)管理器(DTM,協(xié)調(diào)全局事務(wù),進(jìn)行提交以及回滾)
在上述的架構(gòu)圖中,AP通過DTM提供的分布式事務(wù)接口,與RM和TM交互,對現(xiàn)有的微服務(wù),侵入很小。
另外在實(shí)際的業(yè)務(wù)中,AP和RM角色可能會有重疊,例如TCC模式下,AP可能有自己的本地事務(wù),也會注冊并調(diào)用其他事務(wù)分支。