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

news2024/11/16 17:57:49

项目场景:

在项目开发中常常会遇到在一个有数据库操作的方法中,发送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);
    
    // 发送消息给MQ
    sendMQMessage();
}

原因分析

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 commit
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            // 发送消息给MQ
    		sendMQMessage();
        }
    });

}

测试一下,通过日志可以看出事务已经提交了,如何发送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 {

    @Resource
    private UserMapper userMapper;

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT , classes = SendMsgEvent.class)
    public void sendMsg(SendMsgEvent sendMsgEvent) {
        log.info("sendMsg: {}" , JSONUtil.toJsonStr(sendMsgEvent));

         // 发送消息给MQ
    	sendMQMessage();
    }
}

业务类实现业务:

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;

    @Resource
    private UserMapper userMapper;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendMsgAfterRegisterByEvent() {
        User user = doRegister();

        // after transaction commit
        SendMsgEvent 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.coloradmin.cn/o/1163655.html

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

相关文章

stm32 ADC

目录 简介 stm32的adc 框图 ①电压输入范围 ②输入通道 ​编辑③ADC通道 ④ADC触发 ⑤ADC中断 ⑥ADC数据 ⑦ADC时钟 ADC的四种转换模式 hal库代码 标准库代码 简介 自然界的信号几乎都是模拟信号,比如光亮、温度、压力、声音,而为了方便存储、…

容器:软件性能测试的最佳环境

容器总体上提供了一种经济的和可扩展的方法来测试产品在实际情况下的性能,同时还能保持较低的资源成本和开销成本。 软件性能和可伸缩性是我们谈论应用程序开发时经常遇到的话题。一个很大的原因是应用程序的性能和可伸缩性直接影响其在市场上的成功。一个应用程序…

搭建Qt5.7.1+kylinV10开发环境、运行环境

1.下载Qt源码 Index of / 2.编译Qt 解压缩qt-everywhere-opensource-src-5.7.1.tar.gz 进入到qt-everywhere-opensource-src-5.7.1/qtbase/mkspecs这个目录下, 2.1找到以下目录 复制他,然后改名linux-x86-arrch64,博主这里名字取的有些问…

历年网规上午真题(2017年)

解析:D/C 计算机主要性能指标:时钟频率(主频)、运算速度、运算精度、内存大小、数据处理速率(PDR)等 数据库主要指标:最大并发、负载均衡能力、最大连接数等 解析:A 敏捷开发是一种应对快速变化的需求的一种软件开发方法,是一种以人为核心、迭代、循序渐进的开发方…

深度学习之基于Yolov5闯红灯及红绿灯检测系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、闯红灯及红绿灯检测系统![请添加图片描述](https://img-blog.csdnimg.cn/8f260c2ed5ed4d8596e27d38abe42745.jpeg)四. 总结 一项目简介 基于Y…

生成独立运行的QT程序

前言 使用windeployqt程序生成独立运行的QT程序。 方法 1.在QT Creator使用release构建运行一下代码,不使用debug模式,将release文件夹中生成的***.exe文件复制到一个新的文件夹下。 2.打开 Qt 5.14.2(MinGW 7.3.0 64-bit) 进入exe文件所在的目录执…

Java精品项目62基于Springboot+Vue实现的大学生在线答疑平台(编号V62)

Java精品项目62基于SpringbootVue实现的大学生在线答疑平台(编号V62) 大家好,小辰今天给大家介绍一个基于SpringbootVue实现的大学生在线答疑平台(编号V62),演示视频公众号(小辰哥的Java)对号查询观看即可 文章目录 Java精品项目…

Temu新规,12岁以下儿童产品必需提供CPC认证

近日Temu发布儿童用品合规通知:为保障Temu平台消费者的合法权益,保障儿童类商品在目的国的正常销售及合规要求,针对以12岁及以下儿童为主要使用对象的产品, 卖家需提供CPC认证,要求说明: 1、所有产品均需…

linux 下 物理迁移 mysql 数据库 不能启动问题

1、授权问题 # chown -R 777 /app/db/mysql 2、/etc/my.cnf配置问题 [mysqld] basedir/app/db/mysql datadir/app/db/mysql/data socket/app/db/mysql/mysql.sock.lock innodb_buffer_pool_size128M innodb_force_recovery 1 symbolic-links0 [mysqld_safe] log-error/app/…

半方差函数详解

1 引言 托布勒的地理第一定律指出,“一切都与其他事物有关,但近处的事物比远处的事物更相关。 在半变异函数的情况下,更接近的事物更可预测,变异性更小,而遥远的事物则难以预测,相关性也较低。 例如&…

做CSGO游戏搬砖前,这五个问题必须了解

相信经常看我文章的人或多或少都已经了解steam搬砖项目,也叫CSGO游戏搬砖项目,还有人叫它:国外steam游戏汇率差项目,无论怎么称呼,都是同一个项目。虽然我已经实操Steam搬砖项目超过三年,带领了数百名学员踏…

文字间隔css

文字间隔 letter-spacing: 3px

linux入门---线程的同步

目录标题 什么是同步生产者和消费者模型三者之间的关系消费者生产者模型改进生产者消费者模型特点条件变量的作用条件变量有关的函数条件变量的理解条件变量的使用 什么是同步 这里通过一个例子来带着大家了解一下什么是同步,在生活中大家肯定遇到过排队的情景比如…

看完就牛了,自动化测试框架详解

一、引言 随着IT技术的快速发展,软件开发变得越来越快速和复杂化。在这种背景下,传统的手工测试方式已经无法满足测试需求,而自动化测试随之而生。 自动化测试可以提高测试效率和测试质量,减少重复性的测试工作,从而…

移动云:IDC容器安全行业代表,领跑云原生安全技术演进

近日,全球领先的IT电信市场研究和咨询公司IDC发布了《中国容器安全市场洞察,2023》报告(简称《报告》),分析了国内容器安全市场现状以及主要供应商并提供了行动建议。移动云云原生应用安全获得本次IDC报告认可&#xf…

壁纸头像表情包插画流量主小程序开源版开发

壁纸头像表情包插画流量主小程序开源版开发 以下是壁纸头像表情包插画流量主小程序的功能列表: 用户注册和登录:用户可以注册和登录自己的账号,可以使用账号保存自己的喜爱的壁纸、头像、表情包、插画等内容。 壁纸浏览和下载:用…

详解全志R128 RTOS异构多核通信原理

RTOS 异构多核通信 异构多核通信介绍 R128 所带有的 M33 主核心与 C906, HIFI5 DSP 核心是完全不同的核心,为了最大限度的发挥他们的性能,协同完成某一任务,所以在不同的核心上面运行的系统也各不相同。这些不同架构的核心以及他们上面所运…

【基带开发】AD9361 复乘 com_cmpy_a12_b12

IP核 tb_com module tb_com();reg ad9361_l_clk,rst; initial beginad9361_l_clk0;forever #4.545 ad9361_l_clk~ad9361_l_clk; end initial beginrst1;#9.09 rst0; end wire [63 : 0] m_fll_phase_shift_dout; // fll 输出 dout // FLL Phase Shift com_cmpy_a12_b12 FLL_P…

Docker学习——①

文章目录 1、什么是虚拟化、容器化?2、为什么要虚拟化、容器化?3、虚拟化实现方式3.1 应用程序执行环境分层3.2 虚拟化常见类别3.3 常见虚拟化实现3.3.1 主机虚拟化(虚拟机)实现3.3.2 容器虚拟化实现3.3.3 空间隔离实战--基础知识3.3.4 PID 隔离3.3.5 Mo…

如何将各种小程序(微信小程序)项目转换为 uni-app 项目

使用【miniprogram-to-uniapp】可以将微信小程序项目转为 uni-app 项目(新版本 HBuilderX 工具已经支持各种小程序转换插件) HBuilderX 插件地址:miniprogram-to-uniapp v2 - DCloud 插件市场 核心原理:使用 Babel 获取AST(词法分析),然后或…