数据库事务提交后才发送MQ消息解决方案

项目场景:

在项目开发中常常会遇到在一个有数据库操作的方法中,发送MQ消息,如果这种情况消息队列效率比较快,就会出现数据库事务还没提交,消息队列已经执行业务,导致不一致问题。举个应用场景,我们提交一个订单,将流水号放在MQ里,MQ监听到后就会查询订单去做其它业务,如果这时候数据库事务还没提交,也就是没生成订单流水,MQ监听到消息就去执行业务,查询订单,肯定会出现业务不一致问题


问题描述

最近遇到一个业务场景,类似于下单过程,场景是用户注册消息,注册成功后,会发送MQ消息,MQ监听到消息后,会查询用户的信息,如何再做其它业务,但是遇到一个问题,就是mq消费消息的速度是快于数据库事务提交的,就是我们用户注册的信息还没写入数据库,mq已经提前消费了,所以会导致查询不到用户注册的信息

大致的代码:

@Transactional(rollbackFor = Exception.class)
public void register(){User user = User.builder().name("管理员").email("123456@qq.com").build();userMapper.insert(user);// 发送消息给MQsendMQMessage();
}

原因分析

MQ消息消费快于事务提交

在这里插入图片描述


解决方案

对于这种情况,下面给出两种处理方法,一种是借助于Spring框架提供的TransactionSynchronizationManager来控制,另外一种方法是借助于Spring框架提供的@TransactionalEventListener来控制事务

  • TransactionSynchronizationManager控制事务
@Transactional(rollbackFor = Exception.class)
public void register() {User user = User.builder().name("管理员").email("123456@qq.com").build();userMapper.insert(user);// after transaction commitTransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// 发送消息给MQsendMQMessage();}});}

测试一下,通过日志可以看出事务已经提交了,然后发送mq,mq监听到消息,就会去读取用户信息,是可以获取到的

在这里插入图片描述

  • @TransactionalEventListener控制事务

如果借助Spring框架提供的事件监听机制来实现,就需要用到@TransactionalEventListener监听器,下面给出例子

创建一个Event,主要来做参数传送

package com.example.eventlistener.event;import org.springframework.context.ApplicationEvent;public class SendMsgEvent extends ApplicationEvent {private Long userId;private String userName;public SendMsgEvent(Object source){super(source);}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}
}

创建一个监听器,注意要加上@Component,组件类才能被Spring容器管理

package com.example.eventlistener.listener;import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.example.eventlistener.event.SendMsgEvent;
import com.example.eventlistener.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;import javax.annotation.Resource;@Component
@Slf4j
public class SendMsgListener {@Resourceprivate UserMapper userMapper;@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT , classes = SendMsgEvent.class)public void sendMsg(SendMsgEvent sendMsgEvent) {log.info("sendMsg: {}" , JSONUtil.toJsonStr(sendMsgEvent));// 发送消息给MQsendMQMessage();}
}

业务类实现业务:

package com.example.eventlistener.service.impl;import cn.hutool.http.HttpRequest;
import com.example.eventlistener.event.SendMsgEvent;
import com.example.eventlistener.event.UserRegisterEvent;
import com.example.eventlistener.mapper.UserMapper;
import com.example.eventlistener.model.User;
import com.example.eventlistener.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;import javax.annotation.Resource;@Service
@Slf4j
public class UserServiceImpl implements ApplicationEventPublisherAware , IUserService {private ApplicationEventPublisher applicationEventPublisher;@Resourceprivate UserMapper userMapper;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}@Override@Transactional(rollbackFor = Exception.class)public void sendMsgAfterRegisterByEvent() {User user = doRegister();// after transaction commitSendMsgEvent sendMsgEvent = new SendMsgEvent(this);sendMsgEvent.setUserId(user.getId());sendMsgEvent.setUserName(user.getName());applicationEventPublisher.publishEvent(sendMsgEvent);}private User doRegister() {User user = User.builder().name("管理员").email("123456@qq.com").build();userMapper.insert(user);log.info("save user info");return user;}}

经过测试,也可以实现同样的效果,控制数据库的事务提交后,才执行发送MQ消息

在这里插入图片描述

补充:
如果执行出现java.lang.IllegalStateException: Transaction synchronization is not active,说明没加事务控制,加上@Transactional(rollbackFor = Exception.class)即可

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

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

相关文章

为什么 SIEM 是抵御网络威胁的最佳防御手段

随着 IT 服务和基础设施趋向于混合模式,以及最近数据的激增,组织必须拥有一个集中式安全解决方案来跟踪用户的行为和关键安全事件。 威胁行为者越来越善于检测和利用组织网络中的漏洞,网络攻击也在不断发展。虽然管理员可以对已经发生的攻击…

天猫牵手B站双11,入局年轻人的购物圈

11月1日,天猫联动B站知名UP主老番茄,一同共创发布作品《神秘的年更up主,准时回归!》,在视频中除了联动UP主老番茄之外,还有其他3位知名UP主,美食区UP主绵羊料理、数码区UP主老师好我叫何同学、知…

【蓝桥杯软件赛 零基础备赛20周】第2周——常考知识点+判题

文章目录 0. 第1周答疑1. 常考知识点2. 蓝桥杯怎么判题2.1 判题系统如何判题2.2 测试数据和得分的关系2.3 自己做测试数据 3. 备赛计划4. 本周刷题 0. 第1周答疑 问题1:蓝桥杯怎么报名,什么时候报名? 答:集体报名或个人报名。大…

利用maven的dependency插件将项目依赖从maven仓库中拷贝到一个指定的位置

https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html 利用dependency:copy-dependencies可以将项目的依赖从maven仓库中拷贝到一个指定的位置。 使用默认配置拷贝依赖 如果直接执行mvn dependency:copy-dependencies,是将项目…

Qt Creator创建新项目警告问题

这里可以看见如果你是一些高版本会出现各种警告,但是可以编译通过,这是ClangCodeModel模块导致 解决办法 help -> About Plugins..->C ->ClangCodeModel 帮助 -> 关于插件 -> c ->ClangCodeModel取消勾选 然后重启Qt即可

vim三种模式,文本操作(操作字符/光标,列出行号可视化块模式/多文件查看)

目录 vim--文本编辑器 功能 基本概念 命令/默认模式 插入模式 底行模式 文本操作 引入 移动光标位置 删除字符 -- x/dd 复制/粘贴字符 -- yw/yyp 替换文本 -- r / %s 底行模式 全局替换 -- /g 撤销操作 -- u / ctrlr 修改字符 -- cw 示例 跳行 -- ctrlg 底行…

React的useEvent 和 ahooks 的 useMemorizedFn 的深度分析和对比

父组件 const TestParent: React.FC<any> () > {const [State, setState] useState(0);const changeFun useCallback(() > {console.log(useCallback closure 里的 State, State);}, [State]);const changeFun_useEvent useEvent(() > {console.log(useEv…

探索ChatGPT在学术写作中的应用与心得

随着人工智能的迅猛发展&#xff0c;ChatGPT作为一种强大的自然语言处理模型&#xff0c;逐渐在学术界引起了广泛的关注。本文将探讨ChatGPT在学术写作中的应用&#xff0c;并分享使用ChatGPT进行学术写作时的一些经验和心得。 01 — ChatGPT在学术写作中的应用 1.文献综述和…

GoLang忽略文件夹

一、忽略 在使用GoLang开发的过程中&#xff0c;我们可能在搜索查找时&#xff0c;需要屏蔽一些日志文件或者编译文件&#xff0c;基于这样的需求&#xff0c;我们可以在GoLang编辑器中右键选择对应的文件夹-》Mark Directory as-》Ecluded。 这样就可以忽略掉对应的文件夹。 …

双轮差速模型机器人通过线速度、角速度计算机器人位姿

已知上一时刻机器人位置P_OLD (x,y,),机器人当前时刻的线速度和角速度&#xff08;v,&#xff09;,短时间内t内&#xff0c;机器人在线性部分和非线性部分的增量为 线性部分&#xff1a; 非线性部分&#xff1a; 由于可能非常小&#xff0c;导致非线性部分数值不稳定&#xf…

tiki靶机攻略

tiki靶机攻略 扫描 渗透 访问robots.txt 发现目录&#xff0c;访问一下 再次扫描/tiki/目录&#xff0c;然后发现changelog下又tiki的版本信息 kali漏洞搜索&#xff0c;找到一个符合版本的 python3 48927.py 10.4.7.159执行过后&#xff0c;显示不需要密码即可登录 随后bp登…

单链表的查找(按值查找、按位查找)(数据结构与算法)

什么是单链表&#xff1f; 单链表是一种常见的链式数据结构&#xff0c;用于存储和操作数据元素的集合。它由一系列的节点组成&#xff0c;每个节点包含两个部分&#xff1a;数据域和指针域。 单链表的每个节点包含了存储数据的数据域&#xff0c;以及指向下一个节点的指针域。…

深度学习之基于YoloV5的道路地面缺陷检测系统(UI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、道路地面缺陷检测系统四. 总结 一项目简介 基于YoloV5的道路地面缺陷检测系统利用深度学习中的目标检测算法&#xff0c;特别是YoloV5算法&am…

新体验:万圣节夜晚的新游戏!--愤怒的南瓜

引言&#xff1a; Chatgpt4.0 所带来的冲击似乎远超出人们想象&#xff0c;网页小游戏《愤怒的南瓜》在昨日&#xff08;万圣节夜晚&#xff09;火爆了外网。一位名为 Javi Lopez 的外国小哥使用 Midjourney、DALL•E 3 和 GPT-4 打开了一个无限可能的世界&#xff0c;重新演绎…

Git统计个人提交代码行数

目录 一、git bash打开二、查看个人提交的代码行数统计三、查看项目每个人提交的代码行数统计四、查询所有用户的提交总次数五、统计用户一段时间内的提交代码量 在实际开发中&#xff0c;常常会想查看自己对于某个项目的贡献&#xff0c;管理者会查看项目下各成员的贡献&#…

回归预测 | Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测

Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测 目录 Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机的多变量回归…

入门指南|机器人流程自动化(RPA)在数字营销中的8大应用

虽然现在的话题度不及ChatGPT&#xff0c;但近两年最火的MarTech工具非RPA莫属。今天我们就来看看&#xff1a;资本宠儿、号称世界500强中超过70%的企业都在使用、老板心中最佳员工RPA到底是什么&#xff1f;以及在营销与运营中有哪些应用&#xff1f; 01 RPA是什么&#xff1f…

2m照片用手机怎么照?三个方法随心选!

在用手机拍照的时候&#xff0c;我们会发现拍出的照片尺寸都很大&#xff0c;占用手机的存储空间较多&#xff0c;而自己又不需要如此高清晰度的照片&#xff0c;那么如何解决这个问题呢&#xff1f;下面介绍了三种方法。 方法一&#xff1a;调整手机拍照的设置选项 1、打开手…

IDEA使用-通过Database面板访问数据库

文章目录 前言操作过程注意事项1.无法下载驱动2.“Database”面板不显示数据库表总结前言 作为一款强大IDE工具,IDEA具有很多功能,本文将以MariaDB数据库访问为例,详细介绍如何通过IDE工具的Database面板来访问数据库。 操作过程 不同的版本操作会略有差异,这里我们用于演…

[common c/c++] ring buffer/circular buffer 环形队列/环形缓冲区

前言&#xff1a; ring buffer / circular buffer 又名环形队列 / 环形缓冲区&#xff0c;其通过开辟固定尺寸的内存来实现反复复用同一块内存的目的。由于预先开辟了固定尺寸的内容&#xff0c;所以当数据满的时候&#xff0c;可以有两种处理方式&#xff0c;具体使用哪一种按…