【RocketMQ5x-事务消息】实例以及阶段提交方式

前言

最近在捯饬关于开源项目的需求;【整合市面上主流的MQ】;在RocketMq事务消息这部分比较好奇;由此引出的该篇文章;简单介绍下文章内容;

  1. 事务消息的发送方式

    a. SpringBoot实例 (原生实例请看官网)

  2. 事务消息的原理

    a. 阶段提交的方式

    b. 对比区别

贴一下官网:https://rocketmq.apache.org/zh/docs/quickStart/01quickstart

事务消息

直接看代码示例:

/**
 * @author xbhog
 * @date 2024/06/01 16:54
 **/
@Slf4j
@Component
public class TransactionRocketProducer {@Resource
    private RocketMQTemplate rocketMQTemplate;public void sendTransactionMessage(){
        List<String> tags = Arrays.asList("TAG-1", "TAG-2", "TAG-3");
        for (int i = 0; i < 3; i++) {
            Message<String> message = MessageBuilder.withPayload("===>事务消息-" + i).build();
            //destination formats: `topicName:tags` message – message Message arg – ext arg
            TransactionSendResult res = rocketMQTemplate.sendMessageInTransaction("transaction_topic:" + tags.get(i), message, i + 1);
            if (res.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE) && res.getSendStatus().equals(SendStatus.SEND_OK)) {
                log.info("【生产者】事物消息发送成功;成功结果:{}",res);
            }else{
                log.info("【生产者】事务发送失败:失败原因:{}",res);
            }
        }
    }}

在实现事务消息的过程中;绕不过去的是RocketMQLocalTransactionListener ;他是 RocketMQ事务消息中的一个核心接口,用于处理本地事务和消息发送的协调。这个接口定义了在事务消息机制中如何执行和确认本地事务的方法。

该接口有两个方法:

  1. executeLocalTransaction(Message msg, Object arg): 当发送半消息成功后,RocketMQ 会回调这个方法来执行本地事务。在这个方法中,你应该包含你的业务逻辑代码,比如数据库操作等。根据本地事务的执行结果,你需要返回以下三个枚举值之一:

    • LocalTransactionState.COMMIT_MESSAGE: 本地事务执行成功,提交半消息,让消费者可以消费这个消息。
    • LocalTransactionState.ROLLBACK_MESSAGE: 本地事务执行失败,回滚半消息,消息将被丢弃。
    • LocalTransactionState.UNKNOW: 本地事务状态未知,RocketMQ 将会定期回调checkLocalTransaction方法来查询事务状态。
  2. checkLocalTransaction(Message msg): 如果 executeLocalTransaction 方法返回 LocalTransactionState.UNKNOWRocketMQ 会定期调用这个方法来检查本地事务的最终状态。这个方法应该返回最终的本地事务状态,以确定消息是提交还是回滚。

/**
 * @author xbhog
 * @date 2024/06/01 17:05
 **/
@Slf4j
@Component
@RocketMQTransactionListener
public class TranscationRocketListener implements RocketMQLocalTransactionListener {
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
        log.info("执行本地事务");
        String tag = String.valueOf(message.getHeaders().get("rocketmq_TAGS"));
        if ("TAG-1".equals(tag)) {
            //这里只讲TAGA消息提交,状态为可执行
            log.info("【监听器】这里是校验TAG-1;提交状态:COMMIT");
            return RocketMQLocalTransactionState.COMMIT;
        } else if ("TAG-2".equals(tag)) {
            log.info("【监听器】这里是校验TAG-2;提交状态:ROLLBACK");
            return RocketMQLocalTransactionState.ROLLBACK;
        } else if ("TAG-3".equals(tag)) {
            log.info("【监听器】这里是校验TAG-3;提交状态:UNKNOWN");
            return RocketMQLocalTransactionState.UNKNOWN;
        }
        log.info("=========【监听器】提交状态:UNKNOWN");
        return RocketMQLocalTransactionState.UNKNOWN;
    }@Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
        log.info("【监听器】检查本地交易===>{}", message);
        return RocketMQLocalTransactionState.COMMIT;
    }
}

最后实现消费者:

/**
 * @author xbhog
 * @date 2024/06/01 16:54
 **/
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = "transaction-group", topic = "transaction_topic")
public class TransactionRocketConsumer  implements RocketMQListener<String> {@Override
    public void onMessage(String message) {
        log.info("【消费者】===>接收事务消息:{}",message);
    }
}

在实现事务消息的过程,需要注意的点:

  1. 事务消息仅支持在 MessageType 为 Transaction 的主题内使用,即事务消息只能发送至类型为事务消息的主题中,发送的消息的类型必须和主题的类型一致。
  2. NORMAL类型Topic不支持TRANSACTION类型消息,生产消息会报错;需要创建事务Topic

事务消息Topic创建方式: 需要在rocketMQ的bin目录下执行;

./bin/mqadmin updatetopic -n localhost:9876 -t TestTopic -c DefaultCluster -a +message.type=TRANSACTION
  • -c 集群名称
  • -t Topic名称
  • -n nameserver地址
  • -a 额外属性,本例给主题添加了message.type为TRANSACTION的属性用来支持事务消息

阶段提交

RocketMQ 的事务消息实现是基于两阶段提交(2PC,Two-Phase Commit)协议的。这个过程确保了消息生产和本地事务操作的一致性。以下是事务消息处理的两个主要阶段:

  1. 第一阶段(Prepare阶段)

    • 生产者向Broker发送一个“半消息”(也称为Prepared消息),这个消息在Broker中处于暂不投递的状态,即存储在CommitLog中,但在ConsumeQueue中对消费者不可见。
    • 生产者在发送半消息后,立即执行本地事务操作(例如,数据库更新)。
  2. 第二阶段(Commit或Rollback阶段)

    • 根据本地事务操作的结果,生产者向Broker发送一个“提交(Commit)”或“回滚(Rollback)”的通知。

    • 如果本地事务成功,生产者发送Commit请求,Broker则将半消息标记为可投递,消费者之后可以消费该消息。

    • 若本地事务失败,生产者发送Rollback请求,Broker则会丢弃该半消息,确保不会传递无效的事务结果给消费者。

此外,RocketMQ还引入了一个事务状态回查机制,用于处理生产者在第二阶段因异常未能发送Commit或Rollback请求的情况,确保事务的最终一致性。Broker会在一定时间后主动向生产者查询事务状态,从而决定半消息的最终处理结果。

对比区别:

  1. 目标场景不同

    1. 传统2PC: 主要应用于分布式数据库事务,需要所有参与节点达成一致,确保整个事务的原子性。这意味着所有的参与者都需要等待协调者的指令,完成准备阶段后进入提交或回滚阶段,这可能导致较长的阻塞时间。
    2. RocketMQ事务消息: 专注于消息队列的场景,确保消息生产和业务操作的一致性。它并不直接要求所有参与方(如消息消费者)参与到事务中来,而是通过消息的状态控制(预提交、提交、回滚)来保证消息与业务逻辑的最终一致性。
  2. 协议执行流程差异

    1. 传统2PC: 包含两个明确的阶段:准备阶段(Prepare)和提交/回滚阶段(Commit/Rollback)。在准备阶段,所有参与者准备好执行事务,并反馈给协调者;协调者根据所有参与者的反馈决定全局提交或回滚
    2. RocketMQ:准备阶段、提交阶段、回滚阶段、事务回查阶段(如阶段提交流程)
  3. 性能与可用性考量

    1. 传统2PC: 可能因长时间的阻塞等待而影响性能,且对网络稳定性要求极高,任何参与节点的故障都可能导致整个事务失败。
    2. RocketMQ事务消息: 通过异步处理、事务反查等机制减少了阻塞等待,提高了系统的响应速度和可用性。即使在生产者发生故障的情况下,RocketMQ也能通过特定策略(如定时检查事务状态)来保证消息最终被正确处理。
  4. 弹性和扩展性 RocketMQ针对消息队列的特殊需求,设计了更灵活的机制来处理事务消息,比如支持批量消息处理、更高效的故障恢复策略等,这些都有助于提升系统的弹性和扩展能力。

总结

RocketMQ的事务消息设计巧妙地融合了两阶段提交协议的核心理念,同时针对消息队列的特性进行了优化,提供了高度一致性的消息处理机制,既保证了数据的可靠性,又兼顾了高性能与高可用性。通过上述代码实例与深入解析,开发者可以更好地理解和应用RocketMQ事务消息机制,以满足复杂分布式系统中对消息一致性的严格要求。

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值