Spring 事务事件控制 解决业务异步操作解耦 TransactionSynchronizationManager Transaction

2023-11-04

场景

在业务中,经常需要在执行数据库操作后(事务提交完成),发送消息或事件来异步调用其他组件执行相应的业务操作。
比如: 用户注册成功后,发送激活码或激活邮件,如果用户保存后就执行异步操作发送激活码或激活邮件,但是前面用户保存后发生异常,数据库进行回滚,用户实际没有注册成功,但用户收到激活码或激活邮件。此时,我们就迫切要求数据库事务完成后再执行异步操作。

需求

在成功修改数据库后,向Rabbitmq发送一条消息

解决方案

1.使用TransactionSynchronizationManage 控制事务

在这里插入图片描述
TransactionSynchronization 该类中有方法可以控制业务在提交事务后执行。
在这里插入图片描述

service的代码

 public boolean updateById(Hotel hotel) {
        logger.info("----- into service -----");

        //修改DB的数据
        hotelMapper.updateById(hotel);

        //事务提交后再执行
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                //成功修改DB的数据后,向Rabbitmq发消息
                rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId().toString());
                logger.info("----- rabbitmq send message -----");

            }
        });

        logger.info("--------- out service ----------");
        //模拟异常
        int n =0/0;

        return false;
    }

注意: 上面的代码将在事务提交后执行.如果在非事务context中将抛出java.lang.IllegalStateException: Transaction synchronization is not active。
在这里插入图片描述
所以必须在service方法上添加 @Transactional注解。

2. 使用 TransactionSynchronizationAdapter 控制事务

配置类

import com.sun.istack.internal.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Component("afterCommitExecutor")
public class AfterCommitExecutor extends TransactionSynchronizationAdapter implements Executor {
    private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<List<Runnable>>();
    private ThreadPoolExecutor threadPool;

    private Logger logger = LoggerFactory.getLogger(AfterCommitExecutor.class);
    
    @PostConstruct
    public void init() {
        logger.debug("初始化线程池。。。");
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        if (0 >= availableProcessors) {
            availableProcessors = 1;
        }
        int maxPoolSize = (availableProcessors > 5) ? availableProcessors * 2 : 5;
        logger.debug("CPU Processors :%s MaxPoolSize:%s", availableProcessors, maxPoolSize);
        threadPool = new ThreadPoolExecutor(
            availableProcessors,
            maxPoolSize,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(maxPoolSize * 2),
            Executors.defaultThreadFactory(),
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    logger.debug("Task:%s rejected", r.toString());
                    if (!executor.isShutdown()) {
                        executor.getQueue().poll();
                        executor.execute(r);
                    }
                }
            }
        );
    }

    @PreDestroy
    public void destroy() {
        logger.debug("销毁线程池。。。");
        if (null != threadPool && !threadPool.isShutdown()) {
            threadPool.shutdown();
        }
    }

    @Override
    public void execute(@NotNull Runnable runnable) {
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            runnable.run();
            return;
        }
        List<Runnable> threadRunnables = RUNNABLES.get();
        if (threadRunnables == null) {
            threadRunnables = new ArrayList<Runnable>();
            RUNNABLES.set(threadRunnables);
            TransactionSynchronizationManager.registerSynchronization(this);
        }
        threadRunnables.add(runnable);
    }

    @Override
    public void afterCommit() {
        logger.debug("事务提交完成处理 ... ");
        List<Runnable> threadRunnables = RUNNABLES.get();
        for (int i = 0; i < threadRunnables.size(); i++) {
            Runnable runnable = threadRunnables.get(i);
            try {
                threadPool.execute(runnable);
            } catch (RuntimeException e) {
                logger.error("", e);
            }
        }
    }

    @Override
    public void afterCompletion(int status) {
        logger.debug("事务处理完毕 .... ");
        RUNNABLES.remove();
    }
}

service代码

 @Transactional
    public boolean updateById(Hotel hotel) {
        logger.info("----- into service -----");

        //修改DB的数据
        hotelMapper.updateById(hotel);

        // 使用AfterCommitExecutor
        afterCommitExecutor.execute(new Runnable() {
            @Override
            public void run() {
                rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId().toString());
                logger.info("----- rabbitmq send message -----");
            }
        });

        logger.info("--------- out service ----------");
        int n =0/0;

        return false;
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring 事务事件控制 解决业务异步操作解耦 TransactionSynchronizationManager Transaction 的相关文章

随机推荐

  • 提取json字符串中指定格式中的参数值

    直接上代码 import java util ArrayList import java util regex Matcher import java util regex Pattern public class TestDemo pub
  • Linux ./configure --prefix命令

    源码的安装一般由3个步骤组成 配置 configure 编译 make 安装 make install 具体的安装方法一般作者都会给出文档 这里主要讨论配置 configure Configure是一个可执行脚本 它有很多选项 使用命令 c
  • 局域网访问本地localhost-VS2015调试WebService

    两点步骤 一 配置IP 二 VS管理员启动 配置ip 可以自定义IP 或者用自动分配的IP cmd ipconfig VS运行右键 显示所有应用程序 打开配置文件 在
  • 【css学习】使用css3中的var实现主题切换

    一 首先搭建基础的页面结构
  • Spring cloud项目扩展(二)项目集成redis和辅助工具hutool

    最近看到了一个很好用的集成开发工具 里面有很多工具类 可以提高开发效率 官方文档请看 https hutool cn docs 下面主要介绍一下在我们项目中加入工具并且通过这个工具使用redis 话不多说 直接开始 1 在我们原有的项目的项
  • 2021-05-05

    实训3 信息加密与哈希函数 实验目的 理解加密系统的概念 掌握经典加密的主要方法 理解混淆与扩散的概念 掌握DES加密的主要方法 了解非对称加密的重要意义 掌握RSA加密算法的主要思想与使用方法 理解数字签名的作用及生成方法 实验准备及注意
  • java声明方法抛出的异常

    java声明方法抛出的异常 TestExceptions java import java io 异常 public class TestExceptions public static void main String args void
  • 贵阳人文科技学院新颖的计算机毕业设计题目大全50例

    最近要准备毕业设计了 不会选题 希望可以帮忙给一些毕业设计题目 我整整花了一周把之前做的答辩通过的毕业设计成品进行整理如下列表 计算机科学技术毕业设计题目推荐1 10题 1 Springboot美食网站92nn7 2 Springboot基
  • Adb connection Error:远程主机强迫关闭了一个现有的连接

    小编遇到这个烦人的问题 总是一直报错 浏览了许多网页 总结了以下几种解决方法 这些都是转载加上自己的见解 这里本人是用最后一种搞定的 不过有时候需要进入paltform tools目录下 因为没有PATH路径 注意这种方法需要启动虚拟记得就
  • Stable Diffusion公司重磅开源大语言模型StableLM,又爆火了!

    点击下方卡片 关注 CVer 公众号 AI CV重磅干货 第一时间送达 点击进入 gt 计算机视觉 微信技术交流群 金磊 发自 凹非寺转载自 量子位 QbitAI 万万没想到 以文生图著名的Stable Diffusion 也入局了大语言模
  • Java String 字符串 截取保留小数点后两位

    截取保留小数点后两位 public static String dealRateStr String rateStr int i rateStr indexOf 如果没有小数点不 if i 1 return rateStr 00 获取小数点
  • Java设计模式:23种设计模式全面解析,墙都不扶就服你

    命令模式 将命令请求封装为一个对象 使得可以用不同的请求来进行参数化 迭代器模式 一种遍历访问聚合对象中各个元素的方法 不暴露该对象的内部结构 观察者模式 对象间的一对多的依赖关系 仲裁者模式 用一个中介对象来封装一系列的对象交互 备忘录模
  • 关于定时任务简单小脚本

    1 每4小时备份一次 etc 目录至 backup目录中 保存的文件名称格式为 etc yyyy mm dd HH tar xz crontab e 进入编辑模式 tar JcPf创建文档并保存为 xz格式 0 4 tar JcPf bac
  • 深入理解数据结构——图

    include
  • SAC(Soft Actor Critic)学习记录

    SAC Soft Actor Critic 学习记录 基本介绍 SAC Soft Actor Critic 算法在近年来受到了许多的关注 得到了不少深度强化学习研究者的好评 这篇文章主要包含的内容有SAC算法的理论分析和核心代码实现 与许多
  • ProGan,关键创新在于渐进式训练

    2018年 NVIDIA首次用ProGAN解决了这一挑战 研究人员一直无法生成高质量的大图像 例如1024 1024 ProGAN的关键创新在于渐进式训练 它首先用极低分辨率的图像 如4 4 训练生成器和鉴别器 然后每次增加一个更高分辨率的
  • Float比较大小

    目录 起因 结论 科学计数法 十进制转二进制 整数 基本 二进制科学计数法 总结 小数 基本 二进制科学计数法 总结 IEEE754标准存储 基本 起因 在阅读 阿里巴巴开发手册 时发现了一句话 强制 浮点数之间的等值判断 基本数据类型不能
  • Linux(查看服务cpu核数和内存)

    linux服务器中最重要的两个配置是CPU和内存 那么怎么开始查看CPU核数和内存 以及使用情况 是日常运维中使用最多的操作 查看linux服务器cpu最简单的命令是 cat proc cpuinfo 查看linux服务器cpu最简单的命令
  • TCP常见面试题

    1 画出TCP的报头 2 说一下TCP的三次握手过程 3 为什么TCP握手需要三次 TCP是可靠的传输控制协议 三次握手能保证数据可靠传输又能提高传输效率 如果TCP的握手是两次 lt 1 gt 如果client发给server的SYN报文
  • Spring 事务事件控制 解决业务异步操作解耦 TransactionSynchronizationManager Transaction

    场景 在业务中 经常需要在执行数据库操作后 事务提交完成 发送消息或事件来异步调用其他组件执行相应的业务操作 比如 用户注册成功后 发送激活码或激活邮件 如果用户保存后就执行异步操作发送激活码或激活邮件 但是前面用户保存后发生异常 数据库进