MQ本地消息事务表

纯技术方案水文特此记录

MQ本地消息事务表解决了什么问题?

MQ本地事务表方案解决了本地事务与消息发送的原子性问题,即:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。实现本地事务和消息发送的原子性,要么都成功,要么都失败。 简而言之就是保证了生产者发送消息的可靠性

实现流程

整体实现思路并不复杂,代码看起来绕,其实就是新增了一张本地消息表,记录消息发送失败的日志,且随当前业务事务一块提交。等到业务事务执行完毕后,在执行发送MQ逻辑,此时如果MQ发送失败了也不打紧,本地表兜着底呢,后面可以通过定时扫表的方式进行MQ消息的重新发送

从代码实现上来看用到了一些小众的 API ,前提知识储备:事务相关组件 TransactionSynchronizationManager 。通过 TransactionSynchronizationManager 我们可以拿到当前线程是否是事务线程,毕竟事务和线程挂钩。我们还可以通过 TransactionSynchronizationManager 为当前事务线程注册一些事件,例如 afterCommit 事件。当事务执行后触发我们的 afterCommit 事件进行真正的 MQ 消息的发送。

手把手带你debug流程

发来一个请求执行事务方法,事务方法里面处理好业务逻辑后,进行MQ可靠消息发送
在这里插入图片描述
由于可靠MQ消息发送被我们的自定义注解做过 @Around 切面处理,因此先走切面逻辑
在这里插入图片描述
来到切面后,由于此时的所有逻辑还在事务里面,因此并不会触发 joinPoint.proceed() 方法《此方法调用会触发 rocketMQTemplate.send(topic, build) 的执行》。开始调用 secureInvokeService.invoke(record, async) 进行MQ消息发送日志入库到本地表。
在这里插入图片描述
入库完了之后呢?开始注册一个 afterCommit 事件,注意这里仅仅是注册事件,并没有触发执行
在这里插入图片描述
注册完成后直接 return null 。其实这个切面针对事务线程就是一个空壳方法,作用仅仅是保存了一条本地MQ消息发送日志而已
在这里插入图片描述
到这一步已经完成了,本地事务与本地MQ消息表的数据原子性了,此时如果如下代码出现业务逻辑异常,不会触发MQ消息发送逻辑。是正常的,如果MQ本地表入库失败的话,业务逻辑会进行回滚。

@Transactional
public void sendSecureMsg(String testRocketMQ, String message, String ccc) {
    log.info("执行其他的业务逻辑");
    mqProducer.sendSecureMsg(testRocketMQ, message, ccc);
}

等待 sendSecureMsg 方法执行完成我们的自定义切面后,事务切面最终会进行事务的 commit 操作。commit 完成后调用之前注册过的 afterCommit 事件,具体事务源码可以看我主页的这篇文章:
全面解读spring注解事务失效场景,伪代码+图文深度解析spring源码运行过程
在这里插入图片描述
当事务提交后开始发送MQ消息

在这里插入图片描述
最终发MQ。发送失败重试几次,重试还是发送失败改本地MQ表的数据状态等待,定时任务扫描重新发即可。

在这里插入图片描述

通过 method.invoke(bean, args); 触发事务方法,事务会失效,即使此方法被自定义切面加强,由于切面中有判断如果当前线程不在事务内,执行的是方法本身的逻辑,因此走的是如下图圈红的地方
在这里插入图片描述
最终调用如下代码发送MQ

    @SecureInvoke
    public void sendSecureMsg(String topic, Object body, Object key) {
        Message<Object> build = MessageBuilder
                .withPayload(body)
                .setHeader("KEYS", key)
                .build();
        else rocketMQTemplate.send(topic, build);
    }

本文总结

这个方案就是新增了一张本地消息表,记录消息发送失败的日志,且随当前业务事务一块提交。等到业务事务执行完毕后,在执行发送MQ逻辑,此时如果MQ发送失败了也不打紧,本地表兜着底呢,后面可以通过定时扫表的方式进行MQ消息的重新发送

如果还不懂这套方案童鞋可以先去搞懂一下,再来阅读本文效果更佳

  1. 事务方法失效场景。即必须通过代理对象进行的方法调用事务才会生效。
  2. 声明式事务整个的代理对象是如何创建、事务运行的整套流程

全面解读spring注解事务失效场景,伪代码+图文深度解析spring源码运行过程

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用内容,可以得出以下关于MQ消息异常数据库设计的建议: 在处理MQ消息异常时,可以考虑设计一个专门用于记录异常消息的数据库。这个可以包含以下字段: 1. 消息ID:用于唯一标识每条消息的ID。 2. 消息内容:记录消息的具体内容。 3. 异常信息:记录异常的详细信息,例如错误代码、错误描述等。 4. 创建时间:记录消息的创建时间。 5. 处理状态:记录消息的处理状态,例如未处理、处理中、已处理等。 通过这个,可以将发送MQ消息的过程与数据库事务进行解耦,从而实现更好的消息异常处理。当发送MQ消息时出现异常,可以将异常消息记录到这个中,以便后续进行处理。同时,可以在业务处理完成后,将对应的异常消息标记为已处理,以便进行后续的监控和统计。 需要注意的是,在设计数据库时,可以根据具体业务需求进行适当的调整和扩展。例如,可以添加更多的字段来记录消息的来源、目标等信息,以便更好地追踪和分析异常情况。 总结起来,设计一个专门用于记录MQ消息异常的数据库,可以帮助我们更好地处理和监控异常消息,确保消息的可靠性和一致性。 #### 引用[.reference_title] - *1* *2* [相较于RocketMQ事务消息,本地消息才是真正的王者](https://blog.csdn.net/m0_74931226/article/details/127916333)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [【项目】数据库事务MQ发送一致性](https://blog.csdn.net/qq_43103529/article/details/126669449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小咸鱼的技术窝

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值