LVGL新手教程:从零实现一个简单按钮界面
2026/1/13 1:37:48
针对这种跨服务的事务场景,Spring Boot 提供了几种解决方案,按简单程度推荐:
步骤:
<!-- 各服务pom.xml中添加 --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>最新版本</version> </dependency>配置:
# application.yml seata: enabled: true application-id: ${spring.application.name} tx-service-group: my_tx_group service: vgroup-mapping: my_tx_group: default config: type: nacos registry: type: nacos代码实现:
// A服务中 @Service public class AService { @Autowired private BServiceClient bServiceClient; @Autowired private CServiceClient cServiceClient; @GlobalTransactional // 开启全局事务 public void aa() { // 调用B服务 bServiceClient.bb(); // 调用C服务 cServiceClient.cc(); // 如果cc()失败,Seata会自动回滚bb()的事务 } }@Service public class AService { @Autowired private TransactionMQProducer producer; public void aa() { // 发送事务消息 TransactionSendResult sendResult = producer.sendMessageInTransaction( new Message("topic", "bb完成,准备执行cc".getBytes()), null ); } // 本地事务执行器 @RocketMQTransactionListener class TransactionListenerImpl implements RocketMQLocalTransactionListener { @Override public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { try { // 执行B服务调用 bServiceClient.bb(); return RocketMQLocalTransactionState.UNKNOWN; } catch (Exception e) { return RocketMQLocalTransactionState.ROLLBACK; } } @Override public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { // 检查C服务是否执行成功 boolean ccSuccess = checkCCSuccess(); return ccSuccess ? RocketMQLocalTransactionState.COMMIT : RocketMQLocalTransactionState.ROLLBACK; } } }@Service public class AService { public void aa() { try { // 1. 调用B服务 bServiceClient.bb(); // 2. 调用C服务 cServiceClient.cc(); } catch (Exception e) { // 3. 如果C失败,调用B的回滚接口 bServiceClient.rollbackBb(); throw e; } } } // B服务提供回滚接口 @RestController public class BController { @PostMapping("/bb") public void bb() { // 执行数据库操作,但先不提交 // 可以记录操作日志,用于回滚 } @PostMapping("/rollback/bb") public void rollbackBb() { // 根据日志回滚bb操作 } }@Service @Transactional public class AService { public void aa() { // 1. 记录本地事务消息 LocalTransactionMessage message = new LocalTransactionMessage(); message.setStatus("PENDING"); localMessageRepo.save(message); // 2. 调用B服务 bServiceClient.bb(message.getId()); // 3. 调用C服务 try { cServiceClient.cc(); message.setStatus("SUCCESS"); } catch (Exception e) { message.setStatus("FAILED"); // 通过定时任务或手动触发回滚B服务 bServiceClient.compensate(message.getId()); throw e; } } }方案 | 复杂度 | 一致性 | 性能 | 适用场景 |
|---|---|---|---|---|
Seata | 中 | 强一致 | 中 | 金融、订单等强一致场景 |
消息队列 | 中 | 最终一致 | 高 | 异步、高并发场景 |
SAGA | 低 | 最终一致 | 中 | 业务逻辑简单,可补偿 |
本地消息表 | 中 | 最终一致 | 中 | 有本地数据库,可接受延迟 |
如果你想要最简单的实现,推荐使用方案3的SAGA模式:
// 最简单的实现示例 @Service public class AService { @Transactional(rollbackFor = Exception.class) public void aa() { // 1. 先执行本地事务 // ... 本地数据库操作 // 2. 调用B服务 boolean bSuccess = false; String transactionId = null; try { transactionId = bServiceClient.bb(); bSuccess = true; } catch (Exception e) { throw new RuntimeException("B服务调用失败", e); } // 3. 调用C服务 try { cServiceClient.cc(); } catch (Exception e) { // 如果C失败,回滚B if (bSuccess && transactionId != null) { bServiceClient.compensate(transactionId); } throw new RuntimeException("C服务调用失败,已回滚B", e); } } }B服务实现补偿接口:
@RestController public class BController { @PostMapping("/bb") public String bb() { String txId = generateTxId(); // 保存原始数据和事务ID transactionLogRepo.save(txId, getDataToUpdate()); // 执行更新,但标记为待确认 dataRepo.updateWithHold(txId); return txId; } @PostMapping("/compensate/{txId}") public void compensate(@PathVariable String txId) { // 根据txId回滚 Data original = transactionLogRepo.get(txId); dataRepo.rollback(txId, original); } }这种方案不需要额外中间件,实现简单,但需要业务层面对可补偿性有清晰设计。