当前位置: 首页 > news >正文

解决事务提交延迟问题:Spring中的事务绑定事件监听机制解析

目录

  • 一、背景
  • 二、事务绑定事件介绍
  • 三、事务绑定事件原理
  • 四、结语

一、背景

实际工作中碰到一个场景,现存系统有10w张卡需要进行换卡,简单来说就是为用户生成一张新卡,批量换卡申请需要进行审核,审核通过后异步进行处理。

为什么要异步呢?首先是每批次的处理量很大,还有就是换卡的过程需要多次外调第三方,整个事务非常耗时,流程图如下:
批量换卡流程图

这里有个问题,就是审核通过后修改申请记录的状态是一个事务,事务可能没提交,申请记录状态未变更为审核通过,MQ消费端就已经处理了,由于没查询到审核通过的记录,导致操作失败。

那么这个问题怎么解决呢?有同学会说审核通过后延迟几秒发送MQ,但事务提交的时间取决于变更的记录,网络耗时等等,不可控的因素比较多。

最好的方式就是等事务提交后再发送MQ进行异步处理,在Spring中有两种方式进行处理:

  • 编程式事务,借用PlatformTransactionManager平台事务管理器手动提交事务后发送MQ。
  • 声明式事务,通过TransactionalEventListener监听事务是否提交,然后再进行处理,这也是我们今天要介绍的事务绑定事件监听机制。

当然,通过事件发布和订阅的方式,也利于业务代码之间的解耦。


二、事务绑定事件介绍

Spring 4.2版本开始,事件监听器可以绑定到事务的某个阶段,最典型的应用就是当事务完成时再处理事件。注册一个常规的监听器我们可以通过@EventListener来实现。

如果我们需要将事件和事务绑定可以使用 @TransactionalEventListener注解,默认情况下,监听器会绑定到事务的提交阶段。

举个例子:

事务事件发布者:

@Component
public class TransactionalEventPublisher implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher;public void publishCreationEvent(CreationEvent creationEvent) {applicationEventPublisher.publishEvent(creationEvent);}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public static class CreationEvent extends ApplicationEvent {public CreationEvent(Object source) {super(source);}}
}

事务事件监听器:

@Component
public class TransactionalEventListener {@TransactionalEventListener
public void handleOrderCreatedEvent(CreationEvent creationEvent) {}
}

TransactionalEventListener注解暴露了一个phase的属性,通过该属性可以指定监听器绑定到事务的哪个阶段,该属性的值有:

  • BEFORE_COMMIT:事务提交前。
  • AFTER_COMMIT:事务提交后。
  • AFTER_ROLLBACK:事务回滚后。
  • AFTER_COMPLETION:事务完成后(提交或者回滚)。

如果事件发布时没有事务运行,事务监听器不会被调用,但可以通过设置fallbackExecutiontrue来指定即使事件发布时没有事务运行,监听器也会被调用。


三、事务绑定事件原理

正常的ApplicationEvent会在事件发布时同步调用事件监听器进行处理,但是当事件在事务环境中运行发布,且被TransacntionApplicationListener监听时不会直接调用监听器的处理方法,而是会通过回调的方式根据事务所处的阶段进行回调。

那么这种方式到底是怎么实现的呢?

想看实现,先得看监听器的实现,处理事务事件的监听器为TransactionalApplicationListener,它的核心实现为TransactionalApplicationListenerMethodAdapter,从名字就能看出来是里面包含了事务事件处理的处理逻辑。

类图如下:
在这里插入图片描述
主要看onApplicationEvent这个方法,可以看到逻辑也很简单:

  • 如果当前线程有事务绑定,那么会通过TransactionSynchronizationManager事务同步管理器注册一个事务事件监听的同步器。
  • 如果当前线程没有事务绑定,且fallbackExecution属性为true,那么会直接调用父类ApplicationListenerMethodAdapterprocessEvent方法,说白了就是调用事件监听方法。

备注:事务事件监听器也只有在由PlatformTransactionManage平台事务管理器管理的线程绑定的事务中才生效。

@Override
public void onApplicationEvent(ApplicationEvent event) {if (TransactionSynchronizationManager.isSynchronizationActive() &&TransactionSynchronizationManager.isActualTransactionActive()) {TransactionSynchronizationManager.registerSynchronization(new TransactionalApplicationListenerSynchronization<>(event, this, this.callbacks));}else if (this.fallbackExecution) {if (getTransactionPhase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");}processEvent(event);}else {// No transactional event execution at allif (logger.isDebugEnabled()) {logger.debug("No transaction is active - skipping " + event);}}
}

至于TransactionalApplicationListenerSynchronization是啥呢?

其实它就是一个事务同步的回调接口,主要由AbstractPlatformTransactionManager抽象平台事务管理器在事务提交的各个阶段进行调用。

说到平台事务管理器大家肯定都熟悉,它包含了Spring中事务处理基本流程,比如:

  • 当前方法是否有事务运行。
  • 应用指定的隔离级别。
  • 挂起或者恢复事务。
  • 检查提交时的rollback-only标识。
  • 触发注册的同步回调(TransactionSynchronization)。
public interface TransactionSynchronization extends Ordered, Flushable {/** Completion status in case of proper commit. */int STATUS_COMMITTED = 0;/** Completion status in case of proper rollback. */int STATUS_ROLLED_BACK = 1;/** Completion status in case of heuristic mixed completion or system errors. */int STATUS_UNKNOWN = 2;@Overridedefault int getOrder() {return Ordered.LOWEST_PRECEDENCE;}default void suspend() {}default void resume() {}@Overridedefault void flush() {}default void beforeCommit(boolean readOnly) {}default void beforeCompletion() {}default void afterCommit() {}default void afterCompletion(int status) {}}

从上面的方法可以看出,TransactionSynchronization包含了一些事务相关的回调方法,我们看看它的实现TransactionalApplicationListenerSynchronization到底做了什么?

class TransactionalApplicationListenerSynchronization<E extends ApplicationEvent>implements TransactionSynchronization {private final E event;private final TransactionalApplicationListener<E> listener;private final List<TransactionalApplicationListener.SynchronizationCallback> callbacks;public TransactionalApplicationListenerSynchronization(E event, TransactionalApplicationListener<E> listener,List<TransactionalApplicationListener.SynchronizationCallback> callbacks) {this.event = event;this.listener = listener;this.callbacks = callbacks;}@Overridepublic int getOrder() {return this.listener.getOrder();}@Overridepublic void beforeCommit(boolean readOnly) {if (this.listener.getTransactionPhase() == TransactionPhase.BEFORE_COMMIT) {processEventWithCallbacks();}}@Overridepublic void afterCompletion(int status) {TransactionPhase phase = this.listener.getTransactionPhase();if (phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {processEventWithCallbacks();}else if (phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {processEventWithCallbacks();}else if (phase == TransactionPhase.AFTER_COMPLETION) {processEventWithCallbacks();}}private void processEventWithCallbacks() {this.callbacks.forEach(callback -> callback.preProcessEvent(this.event));try {this.listener.processEvent(this.event);}catch (RuntimeException | Error ex) {this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, ex));throw ex;}this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, null));}}

上面的代码也很简单,该类的beforeCommitafterCompletion方法会判断当前事务的状态以及监听器里指定的事务阶段去调用监听器里的业务方法。


四、结语

至此,事务事件监听的实现还是不复杂的,平时工作中也会有很多场景会用到,非常实用,下面是一个简单的流程图:
在这里插入图片描述

在这里插入图片描述

相关文章:

解决事务提交延迟问题:Spring中的事务绑定事件监听机制解析

目录 一、背景二、事务绑定事件介绍三、事务绑定事件原理四、结语 一、背景 实际工作中碰到一个场景&#xff0c;现存系统有10w张卡需要进行换卡&#xff0c;简单来说就是为用户生成一张新卡&#xff0c;批量换卡申请需要进行审核&#xff0c;审核通过后异步进行处理。 为什么…...

Python 异步编程的秘密武器:Asyncio

python编程中&#xff0c;异步编程是一个重要概念。它允许我们在等待某些操作&#xff08;如网络请求或文件读写&#xff09;时&#xff0c;不阻塞程序的其他部分运行。 在 Python 中&#xff0c;asyncio 是实现异步编程的强大工具。今天&#xff0c;我们将一同探索 asyncio 的…...

10年计算机考研408-计算机网络

【题33】下列选项中&#xff0c;不属于网络体系结构所描述的内容是&#xff08;&#xff09; A.网络的层次 B.每一层使用的协议 C.协议的内部实现细节 D.每一层必须完成的功能 解析&#xff1a; 本题考查的是网络体系结构相关的概念。 图1描述了网络的7层架构以及每一层所要完成…...

深信服校招面试总结

许久没有更新博客&#xff0c;这两个月里发生的事情有些多。最近稍微稳定下来了&#xff0c;应该可以重新开始吧。 背景 首先感觉自己的笔试做的还行&#xff0c;除了第三个编程题没做出来&#xff0c;其他的应该都做出来了。当时忘记并查集的路径压缩怎么写了&#xff0c;加上…...

【LeetCode热题100】模拟

这篇博客记录了模拟相关的题目&#xff0c;也就是按照题目的描述写代码&#xff0c;很锻炼代码实现能力&#xff0c;包括了替换所有的问号、Z字形变换、外观数列、数青蛙4道题。 class Solution { public:string modifyString(string s) {int n s.size();for(int i 0 ; i <…...

如何在Chrome最新浏览器中调用ActiveX控件?

小编最近登陆工商银行网上银行&#xff0c;发现工商银行的个人网银网页&#xff0c;由于使用了ActiveX安全控件&#xff0c;导致不能用高版本Chrome浏览器打开&#xff0c;目前只有使用IE或基于IE内核的浏览器才能正常登录网上银行&#xff0c;而IE已经彻底停止更新了&#xff…...

一款好用的远程连接工具:MobaXterm

在日常工作中&#xff0c;作为开发者或运维人员&#xff0c;你是否经常需要远程连接服务器进行调试和管理&#xff1f;传统的SSH工具常常不够灵活&#xff0c;操作繁琐&#xff0c;无法满足日益复杂的工作需求。而MobaXterm的出现&#xff0c;带来了远程连接工具的全新体验。它…...

Spring Boot使用配置方式整合MyBatis

文章目录 一、实战目标二、步骤概览1. 创建部门映射器接口2. 创建映射器配置文件3. 配置全局映射器4. 测试映射器接口 三、详细步骤1、创建部门映射器接口2、创建映射器配置文件3、配置全局映射器4、测试映射器接口 四、结语 一、实战目标 在本实战课程中&#xff0c;我们将学…...

HarmonyOS第一课-应用程序框架基础习题答案

声明&#xff1a;本题库为最新的HarmonyOS第一课的学习题库&#xff0c;仅供参考学习&#xff01; 一、判断题 1. 在基于Stage模型开发的应用项目中都存在一个app.json5配置文件、以及一个或多个module.json5配置文件。&#xff08;正确&#xff09; 正确(True) 错误(False) -…...

滚雪球学SpringCloud[10.2讲]:微服务项目的性能优化与调优

全文目录: 前言性能优化与调优概述性能优化的核心目标常见的性能瓶颈来源 性能瓶颈分析与调优策略1. 服务间通信优化优化策略&#xff1a; 2. 数据库优化优化策略&#xff1a; 3. 线程池优化优化策略&#xff1a; 4. 缓存优化优化策略&#xff1a; 常见问题的排查与解决1. 慢查…...

EasyExcel将数据库里面的数据生成excel文件

EasyExcel官方文档 1.在model模块导入依赖 <!-- 生成报表--> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.3</version> </dependency> 2.修饰实体类 package…...

【YOLO学习】YOLOv1详解

文章目录 1. 概述2. 算法流程3. 网络结构4. 损失函数 1. 概述 1. YOLO 的全称是 You Only Look Once: Unified, Real-Time Object Detection。YOLOv1 的核心思想就是利用整张图作为网络的输入&#xff0c;直接在输出层回归 bounding box 的位置和 bounding box 所属的类别。简单…...

HarmonyOS应用开发(组件库)--组件模块化开发、工具包、设计模式(持续更新)

致力于&#xff0c;UI开发拿来即用&#xff0c;提高开发效率 常量格式枚举enum格式正则表达式...手机号校验...邮箱校验 文件判断文件是否存在 网络下载下载图片从沙箱中图片转为Base64格式从资源文件中读取图片转Base64 组件输入框...矩形输入框...输入框堆叠效果&#xff08;…...

python测试开发---前后端交互Axios

Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;常用于浏览器和 Node.js 中发送 HTTP 请求。它封装了 XMLHttpRequest 和 Node.js 的 http 模块&#xff0c;使得处理网络请求更加简单和直观&#xff0c;尤其适合处理异步请求。以下是 Axios 的基础概念和使用方法&#xf…...

删除视频最后几帧 剪切视频

删除视频最后几帧 剪切视频 remove_last.py import subprocess def remove_last_frame(input_file, output_file, frame_rate):command_duration [ffprobe,-v, error,-show_entries, formatduration,-of, defaultnoprint_wrappers1:nokey1,input_file]try:total_duration fl…...

SSM框架学习(四、SpringMVC实战:构建高效表述层框架)

目录 一、SpringMVC简介和体验 1.介绍 2.主要作用 3.核心组件和调用流程理解 4.快速体验 二、SpringMVC接收数据 1.访问路径设置 &#xff08;1&#xff09;精准路径匹配 &#xff08;2&#xff09;模糊路径匹配 &#xff08;3&#xff09;类和方法上添加 RequestMapp…...

戴尔笔记本电脑——重装系统

说明&#xff1a;我的电脑是戴尔G3笔记本电脑。 第一步&#xff1a;按照正常的装系统步骤&#xff0c;配置并进入U盘的PE系统 如果进入PE系统&#xff0c;一部分的硬盘找不到&#xff0c;解决办法&#xff1a;U盘PE系统——出现部分硬盘找不到的解决办法 第二步&#xff1a;磁…...

领夹麦克风哪个品牌音质最好,主播一般用什么麦克风

在这个信息爆炸的时代&#xff0c;清晰的声音传达显得尤为重要。无论是激情澎湃的演讲&#xff0c;还是温馨动人的访谈&#xff0c;一款优质的无线领夹麦克风都能让声音清晰的传播。但市场上产品繁多&#xff0c;如何挑选出性价比高、性能卓越的无线领夹麦克风呢&#xff1f;本…...

华为静态路由(route-static)

静态路由的组成 在华为路由器中&#xff0c;使用ip route-static命令配置静态路由。 一条静态路由主要包含以下要素&#xff1a; 目的地址&#xff1a;数据包要到达的目标IP地址 子网掩码&#xff1a;用于指定目的地址的网络部分和主机部分 下一跳地址&#xff08;可选&#…...

Focalboard开源项目管理系统本地Windows部署与远程访问协同办公

文章目录 前言1. 使用Docker本地部署Focalboard1.1 在Windows中安装 Docker1.2 使用Docker部署Focalboard 2. 安装Cpolar内网穿透工具3. 实现公网访问Focalboard4. 固定Focalboard公网地址 &#x1f4a1; 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&am…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...