利用事务钩子函数解决业务异步发送问题

利用事务钩子函数解决业务异步发送问题

  • 一、问题背景
  • 二、实现方案
    • 1、生产者代码
    • 2、消费者代码
  • 三、测试与验证
    • 1、未开启事务场景
    • 2、开启事务场景
  • 四、项目结构及源码

一、问题背景

在这里插入图片描述
在某项业务中,需要在事务完成后,写入日志到某数据库中。需要要么都成功,要么都失败,而且需要异步实现。在不考虑分布式事务框架下,如何实现这个业务功能呢?

二、实现方案

前提需要启动kafka_2.12-3.9.1内置的zookeeper和kafka。
在这里插入图片描述
在这里插入图片描述
在kafka创建好topic

kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic testTopic

在这里插入图片描述
在这里插入图片描述
可以利用事务钩子函数实现异步发送,保证同时成功和失败。注册事务钩子,在事务提交或回滚后执行。

if (!TransactionSynchronizationManager.isSynchronizationActive()) {// 调用异步方法发送日志System.out.println("事务未开启");// 异步发送日志(解决由于同一个类内部方法调用不会创建代理,所以aop不生效,则@Async注解无作用问题)kafkaSender.send();} else {// 注册事务钩子,在事务提交或回滚后执行TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {@Overridepublic void afterCompletion(int status) {System.out.println("事务开启并执行完毕");// 异步发送日志(解决由于同一个类内部方法调用不会创建代理,所以aop不生效,则@Async注解无作用问题)kafkaSender.send();}});}

1、生产者代码

KafkaController

@RestController
public class KafkaController {@Autowiredprivate TestService testService;@GetMapping("/send/{type}")public String sendMessageToKafka(@PathVariable int type) {if(type == 1){// 模拟执行事务未开启的业务逻辑testService.executeServiceNoTranscational(type);}else{// 模拟执行事务开启的业务逻辑testService.executeService(type);}//模拟还要执行其他的serviceSystem.out.println("执行其他业务逻辑");return "ok";}
}

KafkaSender

@Component
@Slf4j
public class KafkaSender {@Resourceprivate KafkaTemplate<String, Object> kafkaTemplate;@Asyncpublic void send() {System.out.println("异步发送消息");Map<String, String> messageMap = new HashMap<>();messageMap.put("log", "日志:执行完成");ObjectMapper objectMapper = new ObjectMapper();String data;try {data = objectMapper.writeValueAsString(messageMap);} catch (JsonProcessingException e) {throw new RuntimeException(e);}String key = String.valueOf(UUID.randomUUID());//kakfa的推送消息方法有多种,可以采取带有任务key的,也可以采取不带有的(不带时默认为null)this.send("testTopic", key, data);}public void send(String topic, String key, String data) {//发送消息CompletableFuture<SendResult<String, Object>> completable = kafkaTemplate.send(topic, key, data);completable.whenCompleteAsync((result, ex) -> {if (null == ex) {log.info(topic + "生产者发送消息成功:" + result.toString());} else {log.info(topic + "生产者发送消息失败:" + ex.getMessage());}});}
}

LogService注册钩子函数,异步发送

@Service
public class LogService {@AutowiredKafkaSender kafkaSender;public void sendLogAsync() {if (!TransactionSynchronizationManager.isSynchronizationActive()) {// 调用异步方法发送日志System.out.println("事务未开启");// 异步发送日志(解决由于同一个类内部方法调用不会创建代理,所以aop不生效,则@Async注解无作用问题)kafkaSender.send();} else {// 注册事务钩子,在事务提交或回滚后执行TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {@Overridepublic void afterCompletion(int status) {System.out.println("事务开启并执行完毕");// 异步发送日志(解决由于同一个类内部方法调用不会创建代理,所以aop不生效,则@Async注解无作用问题)kafkaSender.send();}});}}
}

TestService

@Service
public class TestService {@AutowiredLogService logService;@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)public void executeService(int type){System.out.println("执行业务逻辑");/*System.out.println("业务执行完成");if (!TransactionSynchronizationManager.isSynchronizationActive()) {// 调用异步方法发送日志System.out.println("事务未开启");}else{System.out.println("事务已开启");}*/logService.sendLogAsync();}public void executeServiceNoTranscational(int type){System.out.println("业务执行完成");if (!TransactionSynchronizationManager.isSynchronizationActive()) {// 调用异步方法发送日志System.out.println("事务未开启");}else{System.out.println("事务已开启");}logService.sendLogAsync();}
}

2、消费者代码

KafkaConfig

@Configuration
@EnableKafka
public class KafkaConfig {@Beanpublic KafkaReceiver listener() {return new KafkaReceiver();}
}

KafkaReceiver

@Component
@Slf4j
public class KafkaReceiver {/*** 下面的主题是一个数组,可以同时订阅多主题,只需按数组格式即可,也就是用","隔开*/@KafkaListener(topics = {"testTopic"})public void receive(ConsumerRecord<?, ?> record){log.info("消费者收到的消息key: " + record.key());log.info("消费者收到的消息value: " + record.value().toString());}
}

三、测试与验证

1、未开启事务场景

在这里插入图片描述
生产者执行结果
请添加图片描述
请添加图片描述
消费者执行结果
请添加图片描述

2、开启事务场景

生产者执行结果
请添加图片描述
消费者执行结果
请添加图片描述

四、项目结构及源码

在这里插入图片描述

源码下载,欢迎star!

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

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

相关文章

uniapp选择相册

概述 一款针对Android平台下的图片选择器&#xff0c;支持从相册获取图片、视频、音频&拍照&#xff0c;支持裁剪(单图or多图裁剪)、压缩、主题自定义配置等功能&#xff0c;支持动态获取权限&适配Android 5.0系统的开源图片选择框架。 支持Uniapp和Uniapp X下的Vue2、…

MAC 多应用切换技巧,单应用切换技巧

在 Mac 上&#xff0c;有几种快捷键可以帮助你快速切换应用程序窗口&#xff1a; 1. Command (⌘) Tab - 这是最常用的快捷键&#xff0c;用于在打开的应用程序之间进行循环切换。按住 Command 键不放&#xff0c;然后反复按下 Tab 键可以选择下一个应用程序。当你松开 Comm…

SpringBoot+本地部署大模型实现知识库功能

SpringBoot本地部署大模型实现RAG知识库功能 1、Linux系统部署本地大模型1.1 安装ollama1.2 启动ollama1.3 下载deepseek模型 2、Springboot代码调用本地模型实现基础问答功能3、集成向量数据库4、知识库数据喂取5、最终实现RAG知识库功能 1、Linux系统部署本地大模型 1.1 安装…

嵌入式原理与应用篇---ARM

ARM 架构的 STM32 系列微控制器广泛应用于嵌入式系统开发&#xff0c;理解其汇编语言指令对于优化性能、访问硬件底层非常重要。下面详细解释常见的 ARM 汇编指令及其使用实例。 数据处理指令 1. MOV&#xff08;移动&#xff09; 功能&#xff1a;将立即数或寄存器值复制到…

【RHCSA-Linux考试题目笔记(自用)】servera的题目

一、开始 1、启动rhcsa环境 2、点击题目&#xff0c;看题 3、通过控制器来启动所有虚拟机 控制器 打开后点start&#xff0c;然后ok 之后进入一个有classroom、servera、serverb&#xff08;考试不一定叫这些名&#xff0c;但大差不差&#xff09;什么之类的界面&#xff0c;…

SpringBoot项目使用arthas-tunnel-server

参考官网Arthas Spring Boot Starter | arthas Spring Boot系列之使用Arthas Tunnel Server 进行远程调试实践-腾讯云开发者社区-腾讯云 springBoot项目, 增加maven依赖 <dependency><groupId>com.taobao.arthas</groupId><artifactId>arthas-sprin…

Modbus TCP 进阶:基于以太网的远程设备控制(二)

基于 Modbus TCP 的远程设备控制实战 &#xff08;一&#xff09;硬件与网络搭建实操 1. 设备选型与连接 在工业现场&#xff0c;根据远程控制需求进行设备选型至关重要 。对于传感器&#xff0c;若要监测温度&#xff0c;可选择高精度的热电偶传感器&#xff0c;如 K 型热电…

分库分表之实战-sharding-JDBC

大家好&#xff0c;我是工藤学编程 &#x1f989;一个正在努力学习的小博主&#xff0c;期待你的关注实战代码系列最新文章&#x1f609;C实现图书管理系统&#xff08;Qt C GUI界面版&#xff09;SpringBoot实战系列&#x1f437;【SpringBoot实战系列】Sharding-Jdbc实现分库…

httpcore-nio引起的线程、fd泄露问题

依赖来源&#xff1a;httpasyncclient-4.1.4.jar 现象 程序报错too many open files 线程数飙升、句柄数飙升 thread dump显示大量 "I/O dispatcher 7215" #9102 prio5 os_prio0 tid0x00002b7ba036a800 nid0x6f24 runnable [0x00002b7d98d41000]java.lang.Thread.…

多线程生产者消费者模型实战案例

多线程生产者消费者模型实战案例 前言业务场景术前准备无锁无事务有事务 synchronized事务在锁外事务在锁内 数据库行锁什么是数据库行锁有事务没有事务 乐观锁ReentrantLock分布式锁 前言 曾经一直有一个疑惑&#xff0c;就是关于多线程生产者消费者模型的学习过程中&#xf…

青少年编程与数学 02-022 专业应用软件简介 03 三维建模及动画软件:Autodesk Maya

青少年编程与数学 02-022 专业应用软件简介 03 三维建模及动画软件&#xff1a;Autodesk Maya 一、什么是三维建模二、什么是计算机动画三、三维建模及动画设计软件的发展历程&#xff08;一&#xff09;早期探索阶段&#xff08;20世纪60年代 - 80年代&#xff09;&#xff08…

获得 OCM 大师证书学习历练

当我站在山城重庆的洪崖洞前&#xff0c;看着璀璨的夜景倒映在嘉陵江上&#xff0c;手中紧握着 OCM 大师证书&#xff0c;那一刻&#xff0c;备考时的艰辛与考试时的紧张都化作了满满的成就感。这段在重庆获得 OCM 大师证书的经历&#xff0c;就像一场充满挑战与惊喜的冒险&…

srs-gb28181 与 SRS 5.0 对 GB28181 国标支持

srs-gb28181 是基于 SRS 4.0/5.0 的国标&#xff08;GB28181&#xff09;扩展分支&#xff0c;而 SRS 5.0 官方版本也逐步增强了对 GB28181 的支持。以下是两者的主要区别&#xff1a; 1. 功能支持对比 功能srs-gb28181&#xff08;扩展分支&#xff09;SRS 5.0&#xff08;官…

算法第18天|继续二叉树:修剪二叉搜索树、将有序数组转化为二叉搜索树、把二叉搜索树转换为累加树

今日总结&#xff1a; 1、修剪二叉搜索树&#xff08;重点思考如何修剪&#xff09; &#xff08;1&#xff09;递归的返回值是什么&#xff1f;&#xff08;与插入、删除一样&#xff09; &#xff08;2&#xff09;递归的单层逻辑一定要缕清&#xff08;3中情况讨论&#xff…

C# 多线程(三)线程池

目录 1.通过TPL使用线程池 2.不使用TPL进入线程池的办法 异步委托 3.线程池优化技术 最小线程数的工作原理 每当启动一个新线程时&#xff0c;系统都需要花费数百微秒来分配资源&#xff0c;例如创建独立的局部变量栈空间。默认情况下&#xff0c;每个线程还会占用约1…

学习笔记(29):训练集与测试集划分详解:train_test_split 函数深度解析

学习笔记(29):训练集与测试集划分详解&#xff1a;train_test_split 函数深度解析 一、为什么需要划分训练集和测试集&#xff1f; 在机器学习中&#xff0c;模型需要经历两个核心阶段&#xff1a; 训练阶段&#xff1a;用训练集数据学习特征与目标值的映射关系&#xff08;…

【全网唯一】自动化编辑器 Windows版纯本地离线文字识别插件

目的 自动化编辑器超轻量级RPA工具&#xff0c;零代码制作RPA自动化任务&#xff0c;解放双手&#xff0c;释放双眼&#xff0c;轻松玩游戏&#xff0c;刷任务。本篇文章主要讲解下自动化编辑器的TomatoOCR纯本地离线文字识别Windows版插件如何使用和集成。 准备工作 1、下载自…

GitHub 2FA绑定

GitHub 2FA绑定 作为全球最大的代码托管平台&#xff0c;GitHub对账号安全的重视程度不断提升——自2023年3月起&#xff0c;GitHub已要求所有在GitHub.com上贡献代码的用户必须启用双因素身份验证&#xff08;2FA&#xff09;。如果你是符合条件的用户&#xff0c;会收到一封…

pytest fixture基础大全详解

一、介绍 作用 fixture主要有两个作用&#xff1a; 复用测试数据和环境&#xff0c;可以减少重复的代码&#xff1b;可以在测试用例运行前和运行后设置和清理资源&#xff0c;避免对测试结果产生影响&#xff0c;同时也可以提高测试用例的运行效率。 优势 pytest框架的fix…

Unity知识点-Renderer常用材质变量

本篇总结了Unity中renderer的3种常用的材质相关的变量&#xff1a;renderer.material,renderer.sharedMaterial,renderer.MaterialPropertyBlock。以及三者对SRPBatcher的影响。 一.介绍及对比 1.概念介绍 1.material 定义&#xff1a;material 是Render组件&#xff08;如…