解决Spring事务中RPC调用无法回滚的问题

文章目录

    • 问题分析
    • 解决方案
    • 实现原理解析
    • 执行流程说明
    • 运行实例
      • 正常流程执行
      • 执行异常流程
    • 关键优势

在分布式系统开发中,我们经常会遇到本地事务与远程服务调用结合的场景。当本地事务包含RPC调用时,如果事务回滚,RPC调用已经执行就会导致数据不一致。本文将介绍如何优雅地解决这个问题。

问题分析

考虑以下场景:有方法A、B、C组成一个大事务,其中B方法需要调用RPC服务。如果C方法执行失败导致整个事务回滚,但RPC已经调用并提交,就会造成数据不一致。

核心问题是:RPC调用默认会在本地事务提交前执行,无法参与事务回滚

解决方案

Spring提供了事务同步机制(TransactionSynchronization),允许我们注册回调函数,在事务完成后执行特定操作。利用这个机制,我们可以确保:

只有当本地事务成功提交后,才会执行RPC调用;如果事务回滚,则不执行RPC

以下是实现代码:

package cn.bb.mydemo.service;import cn.bb.mydemo.entity.Student;
import cn.bb.mydemo.mapper.StudentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;import java.time.LocalTime;
import java.util.concurrent.CompletableFuture;@Service
public class StudentService {@Autowiredprivate StudentMapper studentMapper;@Transactional(rollbackFor = Exception.class)public void saveWithRpcAfterTx(String name) {// 1. 执行本地数据库操作Student student = new Student();student.setName(name);studentMapper.insert(student);// 2. 注册事务同步回调:在事务提交后异步执行RPCTransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// 使用CompletableFuture异步执行RPC,避免阻塞事务提交CompletableFuture.runAsync(() -> doRpc(name));}});}@Transactional(rollbackFor = Exception.class)public void run(String name) {Student student = new Student();student.setName(name);studentMapper.insert(student);}@Transactional(rollbackFor = Exception.class)public void allRun() {// 调用不同方法组成一个大事务this.saveWithRpcAfterTx("Alice");this.run("a");this.run("b");System.out.println("本地事务已提交");// int i = 1 / 0; // 取消注释此行会触发异常,导致事务回滚}// 模拟RPC调用private void doRpc(String name) {System.out.println(LocalTime.now() + " RPC调用收到:" + name);}
}

测试代码验证实现效果:

@SpringBootTest
class TxAsyncTest {@Autowiredprivate StudentService studentService;@Testvoid shouldRpcAfterTxCommit2() throws InterruptedException {// 调用组合事务方法studentService.allRun();// 等待异步RPC执行完成Thread.sleep(20000);}
}

实现原理解析

这个解决方案的核心在于TransactionSynchronizationManagerTransactionSynchronizationAdapter的使用:

  1. TransactionSynchronizationManager:Spring提供的事务同步管理器,允许注册回调函数监听事务生命周期事件

  2. TransactionSynchronizationAdapter:事务同步适配器,我们可以重写其中的关键方法:

    • afterCommit():事务成功提交后执行
    • afterCompletion():事务完成后执行(无论成功或失败)
    • beforeCommit():事务提交前执行
  3. 异步执行:使用CompletableFuture.runAsync()确保RPC调用不会阻塞事务提交过程

执行流程说明

当调用allRun()方法时,执行流程如下:

  1. 开启数据库事务
  2. 执行saveWithRpcAfterTx():插入数据库记录并注册事务同步回调
  3. 执行run()两次:插入两条额外记录
  4. 如果没有异常,事务提交
  5. 事务提交后,触发注册的afterCommit()回调
  6. 异步执行RPC调用
  7. 如果任何步骤发生异常,事务回滚,不会触发RPC调用

运行实例

正常流程执行

在这里插入图片描述

在这里插入图片描述
新增了ab 并且 rpc是最后被调用的 虽然这个方法被放在了中间

执行异常流程

在这里插入图片描述
在这里插入图片描述
数据也没有添加什么 事务被回滚了 这就保证了我们的需求【当事务被提交 rpc才被调用 否则错误就不执行rpc 防止重复数据的生成】

关键优势

  1. 数据一致性:确保本地事务与RPC调用的最终一致性
  2. 非侵入性:不需要修改现有事务管理代码
  3. 异步执行:不影响事务性能
  4. 简单可靠:基于Spring内置机制,无需额外框架

这种方法特别适合那些需要保证本地数据与远程服务数据一致性的场景,是解决大事务中RPC无法回滚问题的优雅方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92466.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sqli-labs通关笔记-第13关 POST报错型注入(单引号括号闭合 手工注入+脚本注入两种方法)

目录 一、字符型注入 二、limit函数 三、GET方法与POST方法 四、源码分析 1、代码审计 2、SQL注入安全分析 3、报错型注入与联合注入 五、渗透实战 1、进入靶场 2、注入点分析 (1)SQL语句 (2)admin) #注入探测 &…

康复器材动静态性能测试台:精准检测,为康复器械安全保驾护航

在康复医疗领域,无论是轮椅、拐杖、假肢还是康复床,每一件器械的强度与稳定性都直接关系到使用者的安全与康复效果。如何确保这些器械在实际使用中经得起反复考验?Delta德尔塔仪器推出的康复器材动静态性能测试台,凭借其高精度、智…

vue3中el-table表头筛选

效果如下&#xff0c;可以勾选表头进行隐藏&#xff0c;也可以对表头进行拖动排序index主界面 <script> let tempHead []; const showFilter ref<boolean>(false); let tableHeadList ref<TableHeadItem[]>([{ prop: "displayId", label: "…

数据结构 之 【排序】(直接选择排序、堆排序、冒泡排序)

目录 1.直接选择排序 1.1直接选择排序的思想 1.2直接选择排序的代码逻辑 1.3完整排序代码 1.3.1一次只选一个最值 1.3.2一次筛选出两个最值 1.4直接选择排序的时间复杂度与空间复杂度 2.堆排序 2.1堆排序的思想 2.2堆排序的具体步骤 2.3堆排序图解 2.4完整排序代码…

用手机当外挂-图文并茂做报告纪要

前阵参加一个峰会,看到演讲嘉宾每翻一页PPT,下面的观察就举起手机一顿拍。实话说这种拍下来的,难说还会拿出来看,而且再看的时候也未必能对应到当时主讲人的一些解释 。 如果现场将图片保存到笔记本电脑,并快速记录关键信息,这样听完一个报告可能就直接输出一篇报道了。 有…

Vue的ubus emit/on使用

这段代码是 Vue.js 组件中的 mounted 生命周期钩子函数&#xff0c;主要作用是监听一个名为 “macSelectData” 的全局事件。具体行为如下&#xff1a;分步解释&#xff1a;mounted() 生命周期钩子 当组件被挂载到 DOM 后&#xff0c;Vue 会自动调用 mounted() 方法。这里常用于…

rsync报错解决

问题说明 [rootlocalhost shyn]# rsync -avz --checksum "root192.168.159.133:/tmp/shyn" "/tmp /shyn"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! …

ArKTS: DAL,Model,BLL,Interface,Factory using SQLite

HarmonyOS 用ohos.data.rdb 用DBHelper.ets 共用调用SQLite 库&#xff0c;进行DAL,Model,BLL,Interface,Factory 框架模式&#xff0c;表为CREATE TABLE IF NOT EXISTS signInRecord ( id INTEGER PRIMARY KEY AUTOINCREMENT, employeeId TEXT NOT NULL, employeeName TEXT NO…

MySQL JSON 数据类型用法及与传统JSON字符串的对比 JSON数据类型简介

文章目录前言1. 基本用法JSON数据类型 vs 传统JSON字符串1. 存储方式2. 查询方式对比3. 索引支持JSON存储对象和数组的性能考虑1. 存储对象2. 存储数组性能对比总结最佳实践建议前言 MySQL从 5.7 版本开始引入了 JSON 数据类型&#xff0c;专门用于存储 JSON 格式的数据。与传…

C++:list(1)list的使用

list的使用一.list基本的结构1.环状双向链表2.哨兵节点3.迭代器4.节点结构5.链表遍历6.迭代器失效二.list的基本使用1.test01函数&#xff1a;主要测试std::list的初始化方式及遍历2.test02函数&#xff1a;主要测试std::list的常用成员函数操作3.测试结果如下三.list的其他操作…

ArcGIS地形起伏度计算

地形起伏度计算地形起伏度步骤1&#xff1a;计算最大值。步骤2&#xff1a;计算最小值。步骤3&#xff1a;计算地形起伏度。地形起伏度、地形粗糙度、地表切割深度和高程变异系数均为坡面复杂度因子&#xff0c;是一种宏观的地形信息因子&#xff0c;反映的是较大的区域内地表坡…

llama factory新手初步运行完整版

1、新建conda环境名称为llama_factory&#xff0c;并激活 conda create -n llama_factory python3.10 conda activate llama_factory2、激活后可检查内部包是否纯净&#xff0c;要确保环境内包较纯净&#xff0c;不然后续安装对应包会出现一系列水土不服的问题&#xff0c;导致…

Tomcat与JDK版本对照全解析:避坑指南与生产环境选型最佳实践

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…

短视频矩阵的未来前景:机遇无限,挑战并存

在当今数字化信息飞速传播的时代&#xff0c;短视频以其独特的魅力迅速席卷全球&#xff0c;成为人们获取信息、娱乐消遣的重要方式之一。短视频矩阵作为一种高效的内容传播与运营模式&#xff0c;正逐渐展现出其强大的影响力和潜力。本文将深入探讨短视频矩阵的未来前景&#…

【数据结构】哈希——位图与布隆过滤器

目录 位图&#xff1a; 引入 位图实现&#xff1a; 位图的结构 插入数据(标记数据) 删除数据(重置数据) 查找数据 位图完整代码&#xff1a; 位图的优缺点&#xff1a; 布隆过滤器&#xff1a; 引入 布隆过滤器实现&#xff1a; 布隆过滤器的结构&#xff1a; 插入…

本地运行C++版StableDiffusion!开源应用StableVerce发布

本地运行C版StableDiffusion&#xff01;开源应用StableVerce发布 StableVerse是一个用C开发的本地运行的图形工具。适合初学者快速入门&#xff1b;适用于办公室工作人员的文本和图像制作的小规模计算能力场景。 开源地址&#xff1a;https://github.com/kelvin-luo/StableVer…

OpenLayers 快速入门(七)矢量数据

看过的知识不等于学会。唯有用心总结、系统记录&#xff0c;并通过温故知新反复实践&#xff0c;才能真正掌握一二 作为一名摸爬滚打三年的前端开发&#xff0c;开源社区给了我饭碗&#xff0c;我也将所学的知识体系回馈给大家&#xff0c;助你少走弯路&#xff01; OpenLayers…

【PTA数据结构 | C语言版】关于堆的判断

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 将一系列给定数字顺序插入一个初始为空的最小堆。随后判断一系列相关命题是否为真。命题分下列几种&#xff1a; x is the root&#xff1a;x是根结点&#xff1b;x and y are siblings&#xff1a…

[CH582M入门第十步]蓝牙从机

前言 学习目标: 1、初步了解BLE协议 2、BLE从机代码解析 3、使用手机蓝牙软件控制CH582M从机LED亮灭一、蓝牙介绍 蓝牙(Bluetooth)是一种短距离无线通信技术,主要用于设备之间的数据传输和通信。它由爱立信(Ericsson)于1994年提出,现由蓝牙技术联盟(Bluetooth SIG)维…

力扣(LeetCode) ——轮转数组(C语言)

题目&#xff1a;轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例1&#xff1a; 输入&#xff1a; nums [1,2,3,4,5,6,7]&#xff0c;k 3 输出&#xff1a; [5,6,7,1,2,3,4] 解释&#xff1a; 向右轮转 1 步:…