微服務學習(六)之分布式事務
- 一、認識Seata
- 二、部署TC服務
- 1、準備數據庫表
- 2、準備配置文件
- 3、docker部署
- 三、微服務集成seata
- 1、引入依賴
- 2、改造配置
- 3、添加數據庫表
- 4、測試
- 四、XA模式
- 1、兩階段提交
- 2、seata的XA模型
- 3、優缺點
- 4、實現步驟
- 五、AT模式
- 1、Seata的AT模型
- 2、流程梳理
- 3、AT與XA的區別
首先我們看看項目中的下單業務整體流程:
由于訂單、購物車、商品分別在三個不同的微服務,而每個微服務都有自己獨立的數據庫,因此下單過程中就會跨多個數據庫完成業務。而每個微服務都會執行自己的本地事務:
- 交易服務:下單事務
- 購物車服務:清理購物車事務
- 庫存服務:扣減庫存事務
整個業務中,各個本地事務是有關聯的。因此每個微服務的本地事務,也可以稱為分支事務。多個有關聯的分支事務一起就組成了全局事務。我們必須保證整個全局事務同時成功或失敗。
我們知道每一個分支事務就是傳統的單體事務,都可以滿足ACID特性,但全局事務跨越多個服務、多個數據庫,是否還能滿足呢?
事務并未遵循ACID的原則,歸其原因就是參與事務的多個子業務在不同的微服務,跨越了不同的數據庫。雖然每個單獨的業務都能在本地遵循ACID,但是它們互相之間沒有感知,不知道有人失敗了,無法保證最終結果的統一,也就無法遵循ACID的事務特性了。
這就是分布式事務問題,出現以下情況之一就可能產生分布式事務問題:
- 業務跨多個服務實現
- 業務跨多個數據源實現
一、認識Seata
解決分布式事務的方案有很多,但實現起來都比較復雜,因此我們一般會使用開源的框架來解決分布式事務問題。在眾多的開源分布式事務框架中,功能最完善、使用最多的就是阿里巴巴在2019年開源的Seata了。
其實分布式事務產生的一個重要原因,就是參與事務的多個分支事務互相無感知,不知道彼此的執行狀態。因此解決分布式事務的思想非常簡單:
就是找一個統一的事務協調者,與多個分支事務通信,檢測每個分支事務的執行狀態,保證全局事務下的每一個分支事務同時成功或失敗即可。大多數的分布式事務框架都是基于這個理論來實現的。
Seata也不例外,在Seata的事務管理中有三個重要的角色:
- TC (Transaction Coordinator) - 事務協調者:維護全局和分支事務的狀態,協調全局事務提交或回滾。
- TM (Transaction Manager) - 事務管理器:定義全局事務的范圍、開始全局事務、提交或回滾全局事務。
- RM (Resource Manager) - 資源管理器:管理分支事務,與TC交談以注冊分支事務和報告分支事務的狀態,并驅動分支事務提交或回滾。
Seata的工作架構如圖所示:
其中,TM和RM可以理解為Seata的客戶端部分,引入到參與事務的微服務依賴中即可。將來TM和RM就會協助微服務,實現本地分支事務與TC之間交互,實現事務的提交或回滾。
而TC服務則是事務協調中心,是一個獨立的微服務,需要單獨部署。
二、部署TC服務
1、準備數據庫表
Seata支持多種存儲模式,但考慮到持久化的需要,我們一般選擇基于數據庫存儲。執行課前資料提供的《seata-tc.sql》,導入數據庫表:
2、準備配置文件
課前資料準備了一個seata目錄,其中包含了seata運行時所需要的配置文件:
其中包含中文注釋,大家可以自行閱讀。
我們將整個seata文件夾拷貝到虛擬機的/root目錄:
3、docker部署
需要注意,要確保nacos、mysql都在hm-net網絡中。如果某個容器不再hm-net網絡,可以參考下面的命令將某容器加入指定網絡:
docker network connect [網絡名] [容器名]
在虛擬機的/root目錄執行下面的命令:
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.150.101 \ //這里把ip換成自己docker的地址
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2
三、微服務集成seata
1、引入依賴
為了方便各個微服務集成seata,我們需要把seata配置共享到nacos,因此trade-service模塊不僅僅要引入seata依賴,還要引入nacos依賴:
<!--統一配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--讀取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
2、改造配置
首先在nacos上添加一個共享的seata配置,命名為shared-seata.yaml:
內容如下:
seata:registry: # TC服務注冊中心的配置,微服務根據這些信息去注冊中心獲取tc服務地址type: nacos # 注冊中心類型 nacosnacos:server-addr: 192.168.150.101:8848 # nacos地址namespace: "" # namespace,默認為空group: DEFAULT_GROUP # 分組,默認是DEFAULT_GROUPapplication: seata-server # seata服務名稱username: nacospassword: nacostx-service-group: hmall # 事務組名稱service:vgroup-mapping: # 事務組與tc集群的映射關系hmall: "default"
然后,改造trade-service模塊,添加bootstrap.yaml:
內容如下:
spring:application:name: trade-service # 服務名稱profiles:active: devcloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后綴名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置- dataId: shared-seata.yaml # 共享seata配置
可以看到這里加載了共享的seata配置。
然后改造application.yaml文件,內容如下:
server:port: 8085
feign:okhttp:enabled: true # 開啟OKHttp連接池支持sentinel:enabled: true # 開啟Feign對Sentinel的整合
hm:swagger:title: 交易服務接口文檔package: com.hmall.trade.controllerdb:database: hm-trade
參考上述辦法分別改造hm-cart和hm-item兩個微服務模塊。
3、添加數據庫表
seata的客戶端在解決分布式事務的時候需要記錄一些中間數據,保存在數據庫中。因此我們要先準備一個這樣的表。
將課前資料的seata-at.sql分別文件導入hm-trade、hm-cart、hm-item三個數據庫中:
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
OK,至此為止,微服務整合的工作就完成了。可以參考上述方式對hm-item和hm-cart模塊完成整合改造。
4、測試
接下來就是測試的分布式事務的時候了。
我們找到trade-service模塊下的com.hmall.trade.service.impl.OrderServiceImpl類中的createOrder方法,也就是下單業務方法。
將其上的@Transactional注解改為Seata提供的 @GlobalTransactional:
@GlobalTransactional注解就是在標記事務的起點,將來TM就會基于這個方法判斷全局事務范圍,初始化全局事務。
我們重啟trade-service、item-service、cart-service三個服務。再次測試,發現分布式事務的問題解決了!
四、XA模式
Seata支持四種不同的分布式事務解決方案:
- XA
- TCC
- AT
- SAGA
這里我們以XA模式和AT模式來給大家講解其實現原理。
XA 規范 是 X/Open 組織定義的分布式事務處理(DTP,Distributed Transaction Processing)標準,XA 規范 描述了全局的TM與局部的RM之間的接口,幾乎所有主流的數據庫都對 XA 規范 提供了支持。
1、兩階段提交
A是規范,目前主流數據庫都實現了這種規范,實現的原理都是基于兩階段提交。
正常情況:
異常情況:
一階段:
- 事務協調者通知每個事務參與者執行本地事務
- 本地事務執行完成后報告事務執行狀態給事務協調者,此時事務不提交,繼續持有數據庫鎖
二階段:
- 事務協調者基于一階段的報告來判斷下一步操作
- 如果一階段都成功,則通知所有事務參與者,提交事務
- 如果一階段任意一個參與者失敗,則通知所有事務參與者回滾事務
2、seata的XA模型
Seata對原始的XA模式做了簡單的封裝和改造,以適應自己的事務模型,基本架構如圖:
RM一階段的工作:
- 注冊分支事務到TC
- 執行分支業務sql但不提交
- 報告執行狀態到TC
TC二階段的工作:
- TC檢測各分支事務執行狀態
- 如果都成功,通知所有RM提交事務
- 如果有失敗,通知所有RM回滾事務
RM二階段的工作:
- 接收TC指令,提交或回滾事務
3、優缺點
XA模式的優點是什么?
- 事務的強一致性,滿足ACID原則
- 常用數據庫都支持,實現簡單,并且沒有代碼侵入
XA模式的缺點是什么?
- 因為一階段需要鎖定數據庫資源,等待二階段結束才釋放,性能較差
- 依賴關系型數據庫實現事務
4、實現步驟
首先,我們要在配置文件中指定要采用的分布式事務模式。我們可以在Nacos中的共享shared-seata.yaml配置文件中設置:
seata:data-source-proxy-mode: XA
其次,我們要利用@GlobalTransactional標記分布式事務的入口方法:
五、AT模式
AT模式同樣是分階段提交的事務模型,不過缺彌補了XA模型中資源鎖定周期過長的缺陷。
1、Seata的AT模型
基本流程圖:
階段一RM的工作:
- 注冊分支事務
- 記錄undo-log(數據快照)
- 執行業務sql并提交
- 報告事務狀態
階段二提交時RM的工作: - 刪除undo-log即可
階段二回滾時RM的工作: - 根據undo-log恢復數據到更新前
2、流程梳理
我們用一個真實的業務來梳理下AT模式的原理。
比如,現在有一個數據庫表,記錄用戶余額:
其中一個分支業務要執行的SQL為:
update tb_account set money = money - 10 where id = 1
AT模式下,當前分支事務執行流程如下:
一階段:
- TM發起并注冊全局事務到TC
- TM調用分支事務
- 分支事務準備執行業務SQL
- RM攔截業務SQL,根據where條件查詢原始數據,形成快照。
{"id": 1, "money": 100
}
- RM執行業務SQL,提交本地事務,釋放數據庫鎖。此時 money = 90
- RM報告本地事務狀態給TC
二階段:
- TM通知TC事務結束
- TC檢查分支事務狀態
a. 如果都成功,則立即刪除快照
b. 如果有分支事務失敗,需要回滾。讀取快照數據({“id”: 1, “money”: 100}),將快照恢復到數據庫。此時數據庫再次恢復為100
流程圖:
3、AT與XA的區別
簡述AT模式與XA模式最大的區別是什么?
- XA模式一階段不提交事務,鎖定資源;AT模式一階段直接提交,不鎖定資源。
- XA模式依賴數據庫機制實現回滾;AT模式利用數據快照實現數據回滾。
- XA模式強一致;AT模式最終一致
可見,AT模式使用起來更加簡單,無業務侵入,性能更好。因此企業90%的分布式事務都可以用AT模式來解決。