[Java实战]Spring Boot整合Seata:分布式事务一致性解决方案(三十一)
引言
在微服务架构中,业务逻辑被拆分为多个独立的服务,每个服务可能拥有独立的数据库。当需要跨服务操作多个数据库时,如何保证数据的一致性成为关键挑战。Seata(Simple Extensible Autonomous Transaction Architecture)是阿里开源的分布式事务解决方案,支持AT、TCC、SAGA等多种事务模式。本文以 AT模式 为例,结合Spring Boot,讲解如何实现分布式事务的强一致性。
一、分布式事务与Seata原理
1.1 分布式事务的挑战
- CAP理论:在分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)无法同时满足。
- BASE理论:通过牺牲强一致性,实现最终一致性(Basically Available, Soft state, Eventually consistent)。
1.2 Seata的核心概念
- TC(Transaction Coordinator):事务协调器,负责全局事务的提交或回滚。
- TM(Transaction Manager):事务管理器,定义全局事务的边界(如
@GlobalTransactional
)。 - RM(Resource Manager):资源管理器,管理分支事务的资源(如数据库连接)。
1.3 AT模式工作原理
- 阶段一(提交预操作):
- TM向TC注册全局事务。
- RM执行SQL操作,生成前置镜像(before image)和后置镜像(after image),并记录undo_log。
- 阶段二(提交或回滚):
- 全局事务成功:TC异步删除undo_log。
- 全局事务失败:TC根据undo_log反向补偿数据。
二、环境准备
2.1 依赖组件
- Seata Server:1.5.0+
- 注册中心:Nacos(或其他支持的服务发现组件)
- 数据库:MySQL(需支持undo_log表)
2.2 数据库表准备
在每个业务数据库中添加 undo_log
表:
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
三、Docker部署Nacos与Seata Server
3.1 部署Nacos
使用Docker快速启动Nacos服务:
# 拉取Nacos镜像(以2.0.3版本为例)
docker pull docker.1ms.run/nacos/nacos-server:v2.0.3
# 启动Nacos容器(单机模式)
docker run -d \
--name nacos-server \
-p 8848:8848 \
-e MODE=standalone \ # 单机模式
-e JVM_XMS=256m \ # 最小内存
-e JVM_XMX=256m \ # 最大内存
--restart=always \
docker.1ms.run/nacos/nacos-server:v2.0.3
#整合执行
docker run -d --name nacos-server -p 8848:8848 -e MODE=standalone -e JVM_XMS=256m -e JVM_XMX=256m --restart=always docker.1ms.run/nacos/nacos-server:v2.0.3
验证访问:浏览器打开 http://localhost:8848/nacos
,默认账号/密码:nacos/nacos
。
3.2 部署Seata Server
拉取Seata镜像并启动容器:
# 拉取Seata镜像(以1.5.0版本为例)
docker pull docker.1ms.run/seataio/seata-server:1.5.0
# 启动Seata Server容器
docker run -d \
--name seata-server \
-p 8091:8091 \ # Seata TC端口
-p 7091:7091 \ # Seata控制台端口(可选)
-e SEATA_IP=宿主机IP \ # 需替换为实际IP(用于微服务连接)
-e SEATA_CONFIG_NAME=file:/seata-server/resources/registry \
--restart=always \
docker.1ms.run/seataio/seata-server:1.5.0
#整合一行方便执行
docker run -d --name seata-server -p 8091:8091 -p 7091:7091 -e SEATA_IP=192.168.231.132 -e SEATA_CONFIG_NAME=file:/seata-server/resources/registry --restart=always docker.1ms.run/seataio/seata-server:1.5.0
3.3 Docker Compose一键部署 (可选)
创建 docker-compose.yml
:
version: '3'
services:
nacos-server:
image: nacos/nacos-server:v2.0.3
ports:
- "8848:8848"
environment:
- MODE=standalone
networks:
- seata-net
seata-server:
image: seataio/seata-server:1.5.0
ports:
- "8091:8091"
- "7091:7091"
environment:
- SEATA_IP=宿主机IP
volumes:
- ~/seata/config:/seata-server/resources
networks:
- seata-net
depends_on:
- nacos-server
networks:
seata-net:
driver: bridge
启动命令:
docker-compose up -d
3.4 关键配置挂载(可选)
若需自定义Seata配置(如注册中心),可通过挂载配置文件:
- 创建本地目录
~/seata/config
,放入registry.conf
:
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "宿主机IP:8848" # 替换为Nacos容器IP或宿主机IP
namespace = "public"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "宿主机IP:8848"
namespace = "public"
group = "SEATA_GROUP"
}
}
- 启动容器时挂载配置:
docker run -d \
... \
-v ~/seata/config:/seata-server/resources \
seataio/seata-server:1.5.0
四、Spring Boot项目配置
4.1 修改微服务配置
在 application.yml
中,确保Seata和Nacos指向Docker容器地址:
seata:
application-id: order-service # 服务名
tx-service-group: my_tx_group # 事务组名(需与Seata Server配置一致)
service:
vgroup-mapping:
my_tx_group: default # 事务组映射到Seata集群
registry:
type: nacos # 注册中心类型
nacos:
server-addr: 192.168.231.132:8848
namespace: public
config:
type: nacos
nacos:
server-addr: 192.168.231.132:8848
group: SEATA_GROUP
namespace: public
4.2 确保容器间网络互通
若微服务也在Docker中运行,建议使用自定义网络:
# 创建自定义网络
docker network create seata-net
# 启动容器时加入网络
docker run -d --network seata-net --name nacos-server ...
docker run -d --network seata-net --name seata-server ...
此时,微服务容器可通过容器名(如 nacos-server:8848
)访问Nacos和Seata。
五、业务代码与测试
业务代码(如全局事务注解 @GlobalTransactional
)无需修改,与本地部署完全一致。测试步骤参考原文档,重点关注以下验证点:
- 服务注册验证:
- 在Nacos控制台查看
order-service
、seata-server
是否注册成功。
- 事务回滚验证:
- 模拟账户扣款异常,检查订单、库存数据是否回滚。
- 日志排查:
- 查看Seata Server容器日志:
docker logs -f seata-server
- 查看Seata Server容器日志:
六、Spring Boot整合Seata
6.1 项目结构
distributed-transaction-demo
├── order-service # 订单服务
├── storage-service # 库存服务
└── account-service # 账户服务
6.2 添加依赖
在父工程或各子模块的 pom.xml
中添加依赖:
<!-- Seata Starter -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
<!-- Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.3</version> <!-- 使用与你的 Spring Cloud 版本兼容的版本 -->
</dependency>
6.3 配置Seata
在 application.yml
中配置Seata:
seata:
application-id: order-service # 服务名
tx-service-group: my_tx_group # 事务组名(需与Seata Server配置一致)
service:
vgroup-mapping:
my_tx_group: default # 事务组映射到Seata集群
registry:
type: nacos # 注册中心类型
nacos:
server-addr: 192.168.231.132:8848
namespace: public
config:
type: nacos
nacos:
server-addr: 192.168.231.132:8848
group: SEATA_GROUP
namespace: public
6.5 业务代码实现
以订单服务为例,实现分布式事务:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFeignClient storageFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
@GlobalTransactional // 开启全局事务
public void createOrder(Order order) {
// 1. 扣减库存
storageFeignClient.deduct(order.getProductId(), order.getCount());
// 2. 创建订单
orderMapper.insert(order);
// 3. 扣减账户余额
accountFeignClient.deduct(order.getUserId(), order.getMoney());
}
}
七、测试步骤
7.1 启动服务
- 启动 Nacos、Seata Server。
- 依次启动
account-service
、storage-service
、order-service
。
7.2 正常场景测试
使用Postman调用订单服务接口:
POST http://localhost:8080/order/create
Body:
{
"userId": 1,
"productId": 1,
"count": 10,
"money": 100
}
预期结果:订单创建成功,库存和账户余额均扣减。
7.3 异常场景测试
在 accountService.deduct()
方法中模拟异常:
public void deduct(Long userId, BigDecimal money) {
// 模拟异常
if (userId == 1) {
throw new RuntimeException("账户扣款失败!");
}
// 正常扣款逻辑...
}
预期结果:订单未创建,库存扣减回滚,实现数据一致性。
八、常见问题与解决方案
-
Seata Server无法连接Nacos
- 检查Nacos地址和命名空间配置。
- 确保Nacos中已配置
service.vgroupMapping
。
-
undo_log表未生成
- 检查数据库连接配置。
- 确保每个业务库都执行了
undo_log.sql
。
-
事务未回滚
- 确认方法上添加了
@GlobalTransactional
。 - 检查异常是否被捕获(需抛出RuntimeException)。
- 确认方法上添加了
九、总结
通过Seata的AT模式,Spring Boot可以轻松实现分布式事务的强一致性。核心步骤包括配置Seata Server、添加undo_log表、使用 @GlobalTransactional
注解标记全局事务。实际应用中需根据业务场景选择合适的事务模式(如TCC适用于高并发场景)。
附录
- Seata官方文档
希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!