分布式事务-RocketMQ消息事务设计思路及Demo

前言在之前的文章中介绍了基于Seata实现的分布式事务的解决方案(AT、TCC模式),有兴趣的看一下https://blog.csdn.net/hosaos/article/details/89136666这两种方案都是偏向于强一致性解决方案。协调器会不断循环各个资源RM来进行事务的同时提交,回滚。而往往很多场景不求事务的强一致性,只需达到事务的最终一致性,这时候,事务消息可以很好的满...
摘要由CSDN通过智能技术生成

前言

在之前的文章中介绍了基于Seata实现的分布式事务的解决方案(AT、TCC模式),有兴趣的看一下

https://blog.csdn.net/hosaos/article/details/89136666

这两种方案都是偏向于强一致性解决方案。协调器会不断循环各个资源RM来进行事务的同时提交,回滚。

而往往很多场景不求事务的强一致性,只需达到事务的最终一致性,这时候,事务消息可以很好的满足需求。通过将本地事务与消息的发送放在一个本地事务中,来保证,本地事务执行成功时,消息一定被成功投递到消息服务器中,最终利用消息中间件的高可靠性,保证消息会被下游业务所消费

RocketMq 4.3版本中开源了事务消息,本文会以RocketMq为例,介绍事务消息用法、设计思路及原理

思路与问题所在

上面提到了,要保证本地事务与消息的发送在一个事务中,如果以A给B转账100块为例子(A和B分别处在2个微服务中,对应2个数据库),具体怎么做呢?

  • 场景一
    先执行A扣钱100本地事务,再发送给B一条扣钱100消息,行么?
    假设碰到网络问题,消息发送失败了。A扣了100,B却没加钱,肯定不行

  • 场景二
    那先发给B发送一条扣钱100消息,再执行A扣钱本地事务,行么?
    如果消息发送成功了,这时候A服务所在数据库宕机了,岂不是B所在系统消费了消息,B加了100,A却没扣钱,也不对

问题关键点是什么? 只要A扣钱和发送消息不是一个原子操作,即不在一个事务中完成,那么,无论先后顺序如何,都会出现数据不一致性问题

那么聪明的人又会想到,我搞个本地消息表不就行了?

  • 场景三
    在一个事务中,同时操作如下两步
    1、A扣钱100
    2、将要发送的消息记录存入A所在数据库中(如transfer_money_message表)

那么A扣钱成功的同时,一定会有一条对应B扣钱的消息记录在数据库中,然后A所在系统单独启动一个定时器去扫描该消息表,并将状态为待发送的消息,投递到消息服务器中,失败重试,直到消息发送成功

这种方案行不行?当然可以,那么缺点又是什么?显而易见

业务方需要单独设计消息表,及定时发送消息的定时器,增加了与业务无关的开发负担

名词解释

再介绍RocketMq消息事务前,先介绍下几个关键名词

概念 解释
prepare消息 又名Half Message,半消息,标识该消息处于"暂时不能投递"状态,不会被Comsumer所消费,待服务端收到生成者对该消息的commit或者rollback响应后,消息会被正常投递或者回滚(丢弃)消息
RMQ_SYS_TRANS_HALF_TOPIC prepare消息在被投递到Mq服务器后,会存储于Topic为RMQ_SYS_TRANS_HALF_TOPIC的消费队列中
RMQ_SYS_TRANS_OP_HALF_TOPIC 在prepare消息被commit或者rollback处理后,会存储到Topic为RMQ_SYS_TRANS_OP_HALF_TOPIC的队列中,标识prepare消息已被处理

RocketMQ-事务消息设计思路

先抛出两个核心概念:两阶段提交、事务状态定时回查,下面具体说明

两阶段提交

关于两阶段提交的基本概念,本文不再赘述,贴上一张图来说明
在这里插入图片描述

上面已经提到,因为消息发送是一个远程调用,由于网络的不稳定,无法和本地事务的执行处于一个原子操作中,针对这个缺点,RocketMQ基于两阶段提交协议做了如下改动

  • 第一阶段:生产者向MQ服务器发送事务消息(prepare消息),服务端确认后回调通知生产者执行本地事务(此时消息为Prepare消息,存储于RMQ_SYS_TRANS_HALF_TOPIC队列中,不会被消费者消费)

  • 第二阶段:生产者执行完本地事务后(业务执行完成,同时将消息唯一标记,如transactionId与该业务执行记录同时入库,方便事务回查),根据本地事务执行结果,返回Commit/Rollback/Unknow状态码

    1、服务端若收到Commit状态码,则将prepare消息变为提交(正常消息,可被消费者消费)
    2、收到Rollback则对消息进行回滚(丢弃消息)
    3、若状态为Unknow,则等待MQ服务端定时发起消息状态回查,超过一定重试次数或者超时,消息会被丢弃

引用一张流程图来说明消息事务的两阶段提交
在这里插入图片描述

事务状态定时回查

在第二阶段中,生产者在本地事务执行完成后,需要向MQ服务器返回响应状态码,发送状态码的过程也是通过Netty发送网络请求,假设由于网络原因发送失败怎么办?本地事务已经提交/回滚了,但是Commit/Rollback状态码却没发出去,那么MQ服务器上这条prepare消息状态岂不是无法被投递/回滚

因此,MQ服务端会定时扫描存储于RMQ_SYS_TRANS_HALF_TOPIC中的消息,若消息未被处理,则向消费发送者发起回调检查,检查消息对应本地事务执行状态。从而保证消息事务状态最终能和本地事务的状态一致。上图中的5、6、7就是MQ服务端定时回查步骤。

事务消息Demo

先介绍下RocketMQ中事务消息的几个核心类

  1. TransactionMQProducer 事务消息发送者, 核心方法如下
//发送事务消息,arg表示业务参数,能在回调执行本地事务时被取到
  public TransactionSendResult sendMessageInTransaction(final Message msg,
        final Object arg) throws MQClientException {
   
        if (null == this.transactionListener) {
   
            throw new MQClientException("TransactionListener is null", null);
        }

        return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);
   }
   //设置事务状态回调监听器
  public void setTransactionCheckListener(TransactionCheckListener transactionCheckListener) {
   
      this.transactionCheckListener = transactionCheckListener;
  }

2、TransactionCheckListener事务状态回调监听器

/**
     * prepare消息执行成功时,回调执行executeLocalTransaction方法,arg参数为sendMessageInTransaction时带入的业务参数
     *
     * @return Transaction state
     */
    LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);

    /**
     * 检查消息对应本地事务执行状态的监听器,定时回调
     */
    LocalTransactionState checkLocalTransaction(final MessageExt msg);

以用户1向用户2转账100块为例子

设计用户表如下

  • 43
    点赞
  • 155
    收藏
    觉得还不错? 一键收藏
  • 53
    评论
好的,这里简单介绍一下 Java 分布式事务的实现方式以及一个简单的 demo。 Java 分布式事务可以通过两种方式实现:基于 XA 协议的两阶段提交和基于 TCC(Try-Confirm-Cancel)的柔性事务。 XA 协议的两阶段提交是一种经典的分布式事务实现方式,在分布式环境下保证事务的 ACID 特性。它涉及到多个参与者,包括事务协调者(Transaction Coordinator)和多个资源管理器(Resource Manager),在执行分布式事务时,事务协调者会向所有参与者发送 prepare 消息,确认所有的参与者是否可以提交事务。如果所有参与者都可以提交事务,则事务协调者会向所有参与者发送 commit 消息,否则会向所有参与者发送 rollback 消息。 TCC 是一种轻量级的柔性事务实现方式,通过将分布式事务拆分为多个本地事务来实现。在 TCC 中,每个参与者需要实现 try、confirm 和 cancel 三个操作,当事务协调者发起事务时,每个参与者会执行 try 操作,如果所有参与者都执行成功,则事务协调者会执行 confirm 操作,否则会执行 cancel 操作。 下面是一个基于 Spring Cloud 和 TCC 的 Java 分布式事务 demo: https://github.com/changmingxie/tcc-springcloud-example 这个 demo 使用了 Spring Cloud 的 Feign 来实现服务之间的通信,使用了 TCC 模式来实现分布式事务一致性。可以通过阅读代码来了解具体的实现方式和细节。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值