SpringBoot3.2.5整合Seata1.8.0詳細教程
前言
在微服務架構中,分布式事務是一個無法回避的難題。Seata 作為一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。本文將通過一步步的教程,帶你掌握如何在 Spring Boot 3.2.5 項目中整合 Seata 1.8.0,實現(xiàn)分布式事務控制。
環(huán)境準備
在開始整合之前,請確保你的開發(fā)環(huán)境滿足以下要求:
- JDK:17 或更高版本(Spring Boot 3.x 要求 JDK 17+)
- Spring Boot:3.2.5
- Seata:1.8.0
- 注冊中心:Nacos(本文使用 Nacos 作為注冊中心和配置中心)
- 數(shù)據(jù)庫:MySQL 5.7+
- 項目構建工具:Maven 3.6+
Seata 服務端部署
Seata 服務端(TC,即 Transaction Coordinator)是分布式事務的協(xié)調器,我們需要先部署好 Seata Server。
1. 下載 Seata Server
從 Seata 官方 GitHub Release 頁面下載 seata-server-1.8.0 的壓縮包:
wget https://github.com/seata/seata/releases/download/v1.8.0/seata-server-1.8.0.tar.gz tar -zxvf seata-server-1.8.0.tar.gz
2. 配置 Seata Server
Seata 1.8.0 版本使用 application.yml 作為配置文件,位于 seata/conf 目錄下。
2.1 配置注冊中心和配置中心
修改 application.yml,設置使用 Nacos 作為注冊中心和配置中心:
seata:
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: "nacos"
password: "nacos"
data-id: seataServer.properties
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: ""
cluster: default
username: "nacos"
password: "nacos"2.2 配置存儲模式(可選)
如果需要持久化事務日志,可以配置使用數(shù)據(jù)庫存儲。在 Nacos 配置中心創(chuàng)建 data-id 為 seataServer.properties 的配置,添加以下內容:
# 存儲模式 store.mode=db # 數(shù)據(jù)庫配置 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true&rewriteBatchedStatements=true store.db.user=root store.db.password=123456 store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.lockTable=lock_table store.db.queryLimit=100
3. 初始化數(shù)據(jù)庫表
如果使用數(shù)據(jù)庫模式,需要在 MySQL 中創(chuàng)建 Seata 服務端所需的表:
-- 創(chuàng)建數(shù)據(jù)庫 CREATE DATABASE IF NOT EXISTS seata_server; -- 建表腳本位于 seata/script/server/db/ 目錄下 -- 執(zhí)行 mysql.sql 文件 SOURCE /path/to/seata/script/server/db/mysql.sql;
4. 啟動 Seata Server
執(zhí)行啟動腳本:
# Linux/Mac sh ./bin/seata-server.sh # Windows .\bin\seata-server.bat
服務啟動后,默認監(jiān)聽端口:7091(控制臺)和 8091(服務端口)。訪問 Nacos 控制臺,如果看到 seata-server 服務注冊成功,則表示部署完成。
Spring Boot 客戶端整合
接下來,我們將創(chuàng)建兩個 Spring Boot 微服務(訂單服務和庫存服務)來演示分布式事務。
1. 創(chuàng)建 Spring Boot 項目
使用 Spring Initializr 創(chuàng)建兩個 Spring Boot 3.2.5 項目:
- order-service(訂單服務,端口 8081)
- storage-service(庫存服務,端口 8082)
2. 引入依賴
在每個服務的 pom.xml 中添加 Seata 依賴:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Seata Spring Boot Starter -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!-- Nacos 注冊中心客戶端(Seata 注冊需要) -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>3. 客戶端配置
在每個服務的 application.yml 中添加 Seata 配置:
server:
port: 8081 # 訂單服務端口
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# Seata 配置
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: default_tx_group
# 服務端分組映射
service:
vgroup-mapping:
default_tx_group: default
# 注冊中心配置
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: "nacos"
password: "nacos"
# 配置中心(可選)
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: "nacos"
password: "nacos"
data-id: seataClient.properties
# 數(shù)據(jù)源代理(自動開啟)
enable-auto-data-source-proxy: true
# 使用 AT 模式
data-source-proxy-mode: AT4. 創(chuàng)建 undo_log 表
Seata AT 模式需要在每個業(yè)務數(shù)據(jù)庫中創(chuàng)建 undo_log 表,用于記錄事務回滾日志:
-- 注意:在 order_db 和 storage_db 中都執(zhí)行
CREATE TABLE IF NOT EXISTS `undo_log`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) 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 NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';5. 業(yè)務代碼實現(xiàn)
5.1 庫存服務 (Storage Service)
庫存服務提供扣減庫存的接口:
@RestController
@RequestMapping("/storage")
public class StorageController {
@Autowired
private StorageRepository storageRepository;
@PostMapping("/deduct")
public String deduct(@RequestParam Long productId, @RequestParam Integer count) {
Storage storage = storageRepository.findByProductId(productId);
if (storage.getCount() < count) {
throw new RuntimeException("庫存不足");
}
storage.setCount(storage.getCount() - count);
storageRepository.save(storage);
return "扣減成功";
}
}
@Entity
public class Storage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long productId;
private Integer count;
// getters and setters
}5.2 訂單服務 (Order Service)
訂單服務創(chuàng)建訂單并調用庫存服務:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderRepository orderRepository;
@PostMapping("/create")
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public String createOrder(@RequestParam Long productId,
@RequestParam Integer count,
@RequestParam BigDecimal amount) {
// 1. 創(chuàng)建本地訂單
Order order = new Order();
order.setProductId(productId);
order.setCount(count);
order.setAmount(amount);
order.setStatus(0); // 待處理
orderRepository.save(order);
// 2. 遠程調用庫存服務扣減庫存
String url = "http://localhost:8082/storage/deduct?productId=" + productId + "&count=" + count;
String result = restTemplate.postForObject(url, null, String.class);
// 3. 更新訂單狀態(tài)
order.setStatus(1); // 已完成
orderRepository.save(order);
return "訂單創(chuàng)建成功";
}
}注意:@GlobalTransactional 注解是關鍵,它標識該方法需要開啟全局事務。
6. 配置 RestTemplate
在訂單服務中,需要配置 RestTemplate 以實現(xiàn)服務調用,并確保 Seata 的 XID 能夠透傳:
@Configuration
public class SeataRestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 配置 RestTemplate 攔截器,實現(xiàn) XID 傳遞
*/
@Bean
public RestTemplate restTemplate(RestTemplate restTemplate) {
restTemplate.setInterceptors(Collections.singletonList(new RestTemplateInterceptor()));
return restTemplate;
}
/**
* 自定義攔截器,將 Seata 的 XID 放入請求頭
*/
public static class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
String xid = RootContext.getXID();
if (StringUtils.isNotBlank(xid)) {
request.getHeaders().add(RootContext.KEY_XID, xid);
}
return execution.execute(request, body);
}
}
}測試分布式事務
1. 正常流程測試
請求訂單創(chuàng)建接口:
curl "http://localhost:8081/order/create?productId=1&count=2&amount=100"
觀察數(shù)據(jù)庫:
- 訂單表新增一條狀態(tài)為 1 的記錄
- 庫存表對應商品庫存減少 2
2. 異?;貪L測試
修改庫存服務,主動拋出異常:
@PostMapping("/deduct")
public String deduct(@RequestParam Long productId, @RequestParam Integer count) {
Storage storage = storageRepository.findByProductId(productId);
if (storage.getCount() < count) {
throw new RuntimeException("庫存不足");
}
storage.setCount(storage.getCount() - count);
storageRepository.save(storage);
// 模擬異常
if (productId == 1) {
throw new RuntimeException("模擬庫存服務異常");
}
return "扣減成功";
}再次請求訂單創(chuàng)建接口,觀察結果:
- 訂單表新增的記錄狀態(tài)為 0(說明未被更新)
- 庫存表數(shù)量未減少
- undo_log 表中會記錄回滾日志
常見問題及解決方案
1. 版本兼容性問題
Spring Boot 3.2.5 使用了 Jakarta EE 9+(javax 包名改為 jakarta),而 Seata 1.8.0 已經完全支持 Jakarta,無需額外配置。如果遇到類找不到的問題,檢查是否有依賴引入了舊的 javax 包。
2. XID 傳遞失敗
跨服務調用時,如果下游服務無法獲取 XID,事務會失效。解決方案:
- 使用支持 Seata 的微服務組件(如 Spring Cloud Alibaba)
- 手動實現(xiàn)攔截器傳遞 XID(如上面的 RestTemplate 配置)
3. 數(shù)據(jù)源代理沖突
如果項目中同時使用 Druid 等連接池,需要確保 Seata 的數(shù)據(jù)源代理正確執(zhí)行。Seata 的 seata-spring-boot-starter 會自動代理數(shù)據(jù)源,無需額外配置。
4. 注冊中心連接失敗
檢查 Nacos 地址和端口是否正確,確保 Seata Server 已經成功注冊到 Nacos。
總結
本文詳細介紹了 Spring Boot 3.2.5 整合 Seata 1.8.0 的全過程,包括服務端部署、客戶端配置、業(yè)務代碼實現(xiàn)以及測試驗證。Seata 的 AT 模式通過代理數(shù)據(jù)源和記錄 undo_log,對業(yè)務代碼無侵入,是分布式事務接入的首選方案。
在實際生產環(huán)境中,建議:
- 使用數(shù)據(jù)庫模式存儲事務日志,保證數(shù)據(jù)持久化
- 合理設置事務超時時間和重試策略
- 做好監(jiān)控和告警,及時發(fā)現(xiàn)事務異常
到此這篇關于SpringBoot3.2.5整合Seata1.8.0詳細教程的文章就介紹到這了,更多相關SpringBoot整合Seata內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Yml轉properties文件工具類YmlUtils的詳細過程(不用引任何插件和依賴)
這篇文章主要介紹了Yml轉properties文件工具類YmlUtils(不用引任何插件和依賴),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08
詳解Java如何實現(xiàn)與JS相同的Des加解密算法
這篇文章主要介紹了如何在Java中實現(xiàn)與JavaScript相同的DES(Data Encryption Standard)加解密算法,確保在兩個平臺之間可以無縫地傳遞加密信息,希望對大家有一定的幫助2025-04-04
SpringBoot是如何實現(xiàn)自動配置的你知道嗎
這篇文章主要介紹了詳解SpringBoot自動配置原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2021-08-08

