news 2026/4/18 10:11:04

springboot微服务框架中,a服务的aa方法中调用了b服务的bb方法,然后调用c服务的cc方法,由于bb方法会更新数据库,但是需要等到cc方法执行成功,才要把事务提交,如果cc方法不成功,就回滚

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
springboot微服务框架中,a服务的aa方法中调用了b服务的bb方法,然后调用c服务的cc方法,由于bb方法会更新数据库,但是需要等到cc方法执行成功,才要把事务提交,如果cc方法不成功,就回滚

针对这种跨服务的事务场景,Spring Boot 提供了几种解决方案,按简单程度推荐:

方案1:使用分布式事务(推荐)

1.1 集成 Seata(最完整方案)

步骤:

<!-- 各服务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()的事务 } }

方案2:基于消息队列的最终一致性(异步解耦)

2.1 使用 RocketMQ 事务消息

@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; } } }

方案3:SAGA模式(手动编排)

3.1 使用状态机实现

@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操作 } }

方案4:本地消息表(较简单)

@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); } }

这种方案不需要额外中间件,实现简单,但需要业务层面对可补偿性有清晰设计。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:30:41

OBS Face Tracker 面部追踪插件完全指南:实现智能直播追踪

OBS Face Tracker 面部追踪插件完全指南&#xff1a;实现智能直播追踪 【免费下载链接】obs-face-tracker Face tracking plugin for OBS Studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-face-tracker OBS Face Tracker 是一款专为 OBS Studio 设计的革命性面…

作者头像 李华
网站建设 2026/4/17 13:27:56

【工程设计问题】基于PSOGSA、CPSOGSA、GSA、PSO、BBO、DE、ACO、SCA、GWO、CGSA(CHGSA) 10 种智能优化算法求解压力容器设计问题附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f447; 关注我领取海量matlab电子书和数学建模资料 &#x1…

作者头像 李华
网站建设 2026/4/15 14:36:38

Infinigen程序化世界生成:5步打造无限逼真的虚拟环境

Infinigen程序化世界生成&#xff1a;5步打造无限逼真的虚拟环境 【免费下载链接】infinigen Infinite Photorealistic Worlds using Procedural Generation 项目地址: https://gitcode.com/gh_mirrors/in/infinigen 想要快速创建照片级逼真的虚拟世界却苦于手动建模的繁…

作者头像 李华
网站建设 2026/4/18 5:20:03

Java架构从单体到微服务历程

一、前言&#xff1a;架构演进的核心驱动力 Java架构从单体走向微服务&#xff0c;并非技术潮流的盲目跟随&#xff0c;而是企业业务发展到不同阶段的必然选择。核心驱动力主要包括&#xff1a; 业务复杂度提升&#xff1a;从简单的CRUD应用到多模块、多业务线的复杂系统&…

作者头像 李华
网站建设 2026/4/18 8:31:38

LWLP5000差压传感器原理图设计,已量产(压力传感器)

目录 1、电源稳压电路:给传感器 “喂” 足干净的电 2、I2C 电平转换:解决 “电平不兼容” 的痛点 3、传感器接口:把 “高精度” 落到实处 4、调试里的小细节 在智能通风系统、洁净室环境监测这类场景里,差压数据的采集精度直接决定了系统的控制效果 —— 比如洁净室的压…

作者头像 李华