基于本地事务表+MQ实现分布式事务

引言

本地消息表的方案最初由ebay的工程师提出,核心思想是将分布式事务拆分成本地事务进行处理。本地消息表实现最终一致性。本文主要学习mallchat开源项目中的实现方案,向mallchat开发团队致敬!

1、原理

分布式事务解决方案有许多比如二阶段提交、TCC、最大努力通知、Saga事务等,本文介绍本地消息表+MQ这种方式解决分布式事务消息最总一致性问题。目前利用本地消息表+MQ方案实现最终消息一致性的比较多,它的核心思想是,将分布式事务拆分成本地事务进行处理,不同事务之间通过消息表和MQ通信,最后通过定时任务扫描失败的数据进行重试,当在有效重试次数限制内,再次重试回调失败的数据,最终实现消息重复发送,达到一致性。
在这里插入图片描述
在这里插入图片描述

2、本地消息表优缺点

本地消息表实现了分布式事务的最终一致性,优缺点比较明显。
优点
1.实现逻辑简单,开发成本比较低
缺点
1.与业务场景绑定,高耦合,不可公用
2.本地消息表与业务数据表在同一个库,占用业务系统资源,量大可能会影响数据库性能

3、本地启动rocketmq

首先本地启动rocketmq服务。

1:在D:\rocketmq-all-5.3.1-bin-release\bin目录下,执行cmd命令
启动服务
start mqnamesrv.cmd
启动broker
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
关闭服务
mqshutdown namesrv
关闭broker
mqshutdown broker
2:启动rocketmq-dashboard项目,输入127.0.0.1:8080进入可视化界面

其中rocketmq-dashboard可视化代码参考windows下安装启动rocketmq可视化界面
将rocketmq-dashboard导入到idea中,在idea编译启动。
在这里插入图片描述

4、代码实现及验证

4.1、核心代码

(1)注解SecureInvoke

/**
 * 保证方法成功执行。如果在事务内的方法,会将操作记录入库,保证执行。
 *
 * @author hauhua
 * @DATE 2025/1/21
 */
@Retention(RetentionPolicy.RUNTIME)//运行时生效
@Target(ElementType.METHOD)//作用在方法上
public @interface SecureInvoke {
   
   

    /**
     * 默认3次
     *
     * @return 最大重试次数(包括第一次正常执行)
     */
    int maxRetryTimes() default 3;

    /**
     * 默认异步执行,先入库,后续异步执行,不影响主线程快速返回结果,毕竟失败了有重试,而且主线程的事务已经提交了,串行执行没啥意义。
     * 同步执行适合mq消费场景等对耗时不关心,但是希望链路追踪不被异步影响的场景。
     *
     * @return 是否异步执行
     */
    boolean async() default true;
}

(2)自定义切面SecureInvokeAspect

/**
 * 安全执行切面
 *
 * @author hauhua
 * @DATE 2025/1/21
 */
@Slf4j
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE + 1)//确保最先执行
@Component
public class SecureInvokeAspect {
   
   
    @Autowired
    private SecureInvokeService secureInvokeService;

    @Around("@annotation(secureInvoke)")
    public Object around(ProceedingJoinPoint joinPoint, SecureInvoke secureInvoke) throws Throwable {
   
   
        boolean async = secureInvoke.async();
        boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();
        // 非事务状态,直接执行,不做任何保证。
        if (!inTransaction) {
   
   
            return joinPoint.proceed();
        }
        // 如果是事务状态,保证保存到本地消息表中的数据和保存在数据库中数据,在同一个事务内
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        List<String> parameters = Stream.of(method.getParameterTypes()).map(Class::getName).collect(Collectors.toList());
        SecureInvokeVo dto = SecureInvokeVo.builder()
                .args(JsonUtils.toStr(joinPoint.getArgs()))
                .className(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(JsonUtils.toStr(parameters
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值