微服务架构下,一个业务操作跨多个服务和数据库,本地事务无法保证全局一致性。Saga模式通过将长事务拆分为一系列本地事务,配合补偿操作实现最终一致性。Seata框架提供了生产级Saga实现,本文以订单支付场景为例,完整演示Spring Boot整合Seata的落地过程。
Saga模式与Seata架构
Saga将分布式事务拆分为N个本地事务T1、T2…Tn,每个本地事务有对应的补偿操作C1、C2…Cn。执行顺序为T1→T2→…→Tn,如果第i步失败,则执行Ci-1→Ci-2→…→C1回滚。与XA两阶段提交不同,Saga没有全局锁,各本地事务独立提交,通过补偿达到最终一致。
Seata的Saga模式基于状态机引擎(StateMachineEngine),通过JSON定义事务流程,每个节点对应一个服务调用和补偿调用。核心组件包括:TC(Transaction Coordinator)事务协调器,TM(Transaction Manager)事务管理器,RM(Resource Manager)资源管理器。
Seata Server部署与Nacos注册
Seata Server作为TC独立部署,使用Nacos作为注册中心和配置中心:
# application.yml (Seata Server)
seata:
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: seata
data-id: seataServer.properties
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: seata
store:
mode: db
db:
url: jdbc:mysql://127.0.0.1:3306/seata
user: root
password: your_password
store.mode设为db保证事务日志持久化,重启不丢失。Nacos中配置seataServer.properties,包含事务组到集群的映射关系。Seata Server 2.x版本默认使用AT模式,Saga模式需要单独启用状态机引擎。
Spring Boot微服务接入Seata Saga
三个微服务:订单服务(order-service)、库存服务(inventory-service)、账户服务(account-service)。以订单服务为TM发起全局事务。Maven依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-saga-statemachine</artifactId>
<version>2.0.0</version>
</dependency>
application.yml配置事务组:
seata:
tx-service-group: order_tx_group
service:
vgroup-mapping:
order_tx_group: default
saga:
enable: true
state-machine:
enable: true
Saga状态机定义与补偿逻辑
状态机JSON定义文件放在resources/statelang/目录下,描述事务流程中的每个步骤及其补偿操作:
{
"Name": "orderPaymentSaga",
"Comment": "订单支付Saga事务",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "create",
"CompensateState": "CompensateCreateOrder",
"Next": "DeductInventory"
},
"CompensateCreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "cancel"
},
"DeductInventory": {
"Type": "ServiceTask",
"ServiceName": "inventoryService",
"ServiceMethod": "deduct",
"CompensateState": "CompensateDeductInventory",
"Next": "DeductAccount"
},
"CompensateDeductInventory": {
"Type": "ServiceTask",
"ServiceName": "inventoryService",
"ServiceMethod": "addBack"
},
"DeductAccount": {
"Type": "ServiceTask",
"ServiceName": "accountService",
"ServiceMethod": "deduct",
"CompensateState": "CompensateDeductAccount",
"Next": "Succeed"
},
"CompensateDeductAccount": {
"Type": "ServiceTask",
"ServiceName": "accountService",
"ServiceMethod": "refund"
},
"Succeed": {
"Type": "Succeed"
}
}
}
如果DeductAccount步骤失败(余额不足),状态机自动执行CompensateDeductInventory和CompensateCreateOrder,将库存和订单恢复到初始状态。每个ServiceTask的ServiceName对应Spring Bean名称,ServiceMethod为方法名。
业务代码实现与事务发起
订单服务中通过StateMachineEngine发起Saga事务:
@Service
public class OrderSagaService {
@Autowired
private StateMachineEngine stateMachineEngine;
public String processOrder(OrderDTO order) {
Map<String, Object> params = new HashMap<>();
params.put("orderId", order.getId());
params.put("userId", order.getUserId());
params.put("productId", order.getProductId());
params.put("amount", order.getAmount());
params.put("money", order.getMoney());
StateMachineInstance instance = stateMachineEngine.start(
"orderPaymentSaga",
BusinessKeyGenerator.generate(order.getId()),
params
);
if (instance.getStatus() == ExecutionStatus.SU) {
return "订单处理成功: " + order.getId();
} else {
throw new RuntimeException("订单处理失败,已执行补偿: "
+ instance.getException());
}
}
}
各服务的业务方法需保证幂等性,因为网络超时可能导致重试调用。补偿方法同样需要幂等,同一个补偿可能被多次执行。实现幂等的常见方案:使用唯一请求ID + 数据库唯一索引,或通过状态字段判断是否已处理。
// 库存服务 - 扣减与补偿
@Service
public class InventoryService {
@Transactional
public boolean deduct(Long productId, Integer amount, String txId) {
// 幂等检查
if (txLogRepository.existsByTxId(txId)) {
return true;
}
int rows = inventoryRepository.deductStock(productId, amount);
if (rows == 0) {
throw new RuntimeException("库存不足");
}
txLogRepository.save(new TxLog(txId, "DEDUCT", productId, amount));
return true;
}
@Transactional
public boolean addBack(Long productId, Integer amount, String txId) {
if (txLogRepository.existsByTxId(txId + "_COMP")) {
return true;
}
inventoryRepository.addBackStock(productId, amount);
txLogRepository.save(new TxLog(txId + "_COMP", "ADD_BACK", productId, amount));
return true;
}
}
事务监控与异常处理
Seata控制台提供事务全链路可视化,可查看每个Saga实例的执行状态、各步骤耗时和补偿记录。生产环境中关注以下指标:事务成功率、平均执行时间、补偿触发率。补偿触发率持续偏高说明业务逻辑存在设计缺陷,需排查具体失败步骤。
超时配置影响Saga整体执行时间。全局超时建议设为各步骤超时之和的1.5倍,避免极端场景下事务长时间挂起。Seata Server的retry策略对网络抖动场景下的自动恢复至关重要,重试次数建议设为3次,间隔5秒。对于不可补偿的操作(如调用第三方支付接口),放在Saga流程的最后一步执行,减少回滚需求。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-bu-shi-shi-wu-saga-mo-shi-luo-di-springboot-zheng-he/