一、概述
在微服務架構下,雖然我們會盡量避免分布式事務,但是只要業務復雜的情況下這是一個繞不開的問題,如何保證業務數據一致性呢?本文主要介紹同步場景下使用Seata
的AT模式
來解決一致性問題。
Seata
是 阿里巴巴 開源的 一站式分布式事務解決方案 中間件,以 高效 並且對業務 0 侵入 的方式,解決 微服務 場景下面臨的分布式事務問題
二、Seata介紹
整體事務邏輯是基於 兩階段提交 的模型,核心概念包括以下3個角色:
- TM:事務的發起者。用來告訴 TC,全局事務的開始,提交,回滾。
- RM:具體的事務資源,每一個 RM 都會作為一個分支事務注冊在 TC。
- TC:事務的協調者seata-server,用於接收我們的事務的注冊,提交和回滾。
目前的Seata
有兩種模式可使用分別對應不同業務場景
2.1. AT模式
該模式適合的場景:
- 基於支持本地
ACID
事務的關系型數據庫。 - Java 應用,通過
JDBC
訪問數據庫。
一個典型的分布式事務過程:
TM
向TC
申請開啟一個全局事務,全局事務創建成功並生成一個全局唯一的XID
。XID
在微服務調用鏈路的上下文中傳播。RM
向TC
注冊分支事務,將其納入 XID 對應全局事務的管轄。TM
向TC
發起針對XID
的全局提交或回滾決議。TC
調度XID
下管轄的全部分支事務完成提交或回滾請求。
2.2. MT模式
該模式邏輯類似TCC
,需要 自定義實現 prepare
、commit
和rollback
的邏輯,適合 非關系型數據庫 的場景
三、Seata場景樣例
模擬一個簡單的用戶下單場景,4個子工程分別是 Bussiness(事務發起者)、Order(創建訂單)、Storage(扣減庫存) 和 Account(扣減賬戶余額)
3.1. 部署Seata的Server端
Discover
注冊、Config
配置和Store
存儲模塊默認都是使用file
只能適用於單機,我們安裝的時候分別改成使用nacos
和Mysql
以支持server端集群
3.1.1. 下載最新版本並解壓
https://github.com/seata/seata/releases
3.1.2. 修改 conf/registry.conf 配置
注冊中心和配置中心默認是file
這里改為nacos
;設置 registry 和 config 節點中的type
為nacos
,修改serverAddr
為你的nacos
節點地址。
registry {
type = "nacos"
nacos {
serverAddr = "192.168.28.130"
namespace = "public"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "192.168.28.130"
namespace = "public"
cluster = "default"
}
}
3.1.3. 修改 conf/nacos-config.txt配置
- 修改 service.vgroup_mapping 為自己應用對應的名稱;如果有多個服務,添加相應的配置
默認組名為
${spring.application.name}-fescar-service-group
,可通過spring.cloud.alibaba.seata.tx-service-group
配置修改
- 修改 store.mode 為
db
,並修改數據庫相關配置
3.1.4. 初始化seata的nacos配置
cd conf
sh nacos-config.sh 192.168.28.130
成功后在nacos
的配置列表中能看到seata
的相關配置
3.1.5. 初始化數據庫
執行conf/db_store.sql
中的腳本
3.1.6. 啟動seata-server
sh bin/seata-server.sh -p 8091 -h 192.168.28.130
3.2. 應用配置
3.2.1. 初始化數據庫
執行腳本 seata-demo.sql
需在業務相關的數據庫中添加 undo_log 表,用於保存需要回滾的數據
3.2.2. 添加registry.conf配置
直接把 seata-server 中的registry.conf
復制到每個服務中去即可,不需要修改
3.2.3. 修改配置
demo中的每個服務各自修改配置文件
- bootstrap.yml 修改nacos地址
- application.yml 修改數據庫配置
3.2.4. 配置數據源代理
Seata
是通過代理數據源實現分布式事務,所以需要配置io.seata.rm.datasource.DataSourceProxy
的Bean
,且是@Primary
默認的數據源,否則事務不會回滾,無法實現分布式事務
public class DataSourceProxyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}
因為使用了mybatis的starter所以需要排除DataSourceAutoConfiguration
,不然會產生循環依賴
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3.2.5. 事務發起者添加全局事務注解
事務發起者 business-service
添加 @GlobalTransactional
注解
@GlobalTransactional
public void placeOrder(String userId) {
......
}
3.3. 測試
提供兩個接口測試
- 事務成功:扣除庫存成功 > 創建訂單成功 > 扣減賬戶余額成功
http://localhost:9090/placeOrder - 事務失敗:扣除庫存成功 > 創建訂單成功 > 扣減賬戶余額失敗,事務回滾
http://localhost:9090/placeOrderFallBack
3.4. demo下載地址
https://gitee.com/zlt2000/microservices-platform/tree/master/zlt-demo/seata-demo
推薦閱讀
- 日志排查問題困難?分布式日志鏈路跟蹤來幫你
- zuul集成Sentinel最新的網關流控組件
- 阿里注冊中心Nacos生產部署方案
- Spring Boot自定義配置項在IDE里面實現自動提示
- Spring Cloud Zuul的動態路由怎樣做?集成Nacos實現很簡單
- Spring Cloud開發人員如何解決服務沖突和實例亂竄?
掃碼關注有驚喜!