Alibaba Spring Cloud 二十四 RocketMQ事务消息

在分布式系统中,往往会遇到需要保证跨服务、跨系统的数据一致性场景。通常可以采用分布式事务的方式来保证数据一致,但传统的两阶段提交(XA 等)在性能和复杂度上都较为昂贵。RocketMQ 通过事务消息 (Transaction Message) 提供了一种最终一致性解决方案,将本地事务与消息发送进行半同步耦合,可在业务上实现类似“分布式事务”的效果。

本文将为你介绍 RocketMQ 事务消息的原理、使用流程、关键配置与常见注意事项,并结合示例说明如何在实际项目中应用。


1. 事务消息的核心原理

RocketMQ 事务消息与普通消息不同之处在于,它引入了“三段式”提交流程:

  1. 发送半消息 (Half Message)

    • Producer 首先向 Broker 发送一个“半消息(Half Message)”,Broker 会先把这条消息持久化,但并不对消费者可见。
    • 同时返回给 Producer 一个发送结果(其中包含 msgId 等信息)。此时对 Consumer 而言,这条消息并不存在于其可消费范围内。
  2. 执行本地事务

    • Producer 在收到 Half Message 成功发送的响应后,本地开始执行自己的业务逻辑/事务操作(如数据库更新、扣减库存等)。
    • 执行完成后,需要根据结果告诉 Broker:是要提交还是回滚该消息。
  3. 提交或回滚消息

    • 如果本地事务成功,Producer 会向 Broker 发送一个“commit”请求,Broker 会把该半消息标记为可见,变为真正的消息,Consumer 就能消费到它。
    • 如果本地事务失败,则发送“rollback”请求,Broker 会将半消息删除或忽略,从而不会被消费者看到。

为了防止 Producer 因故障(如宕机)而没有及时发送“commit”或“rollback”,Broker 引入了事务回查 (Transaction Check) 机制:

  • Broker 会定期向 Producer 询问(回查)未完成状态的半消息,Producer 必须实现相应的回查接口,从而再次判定本地事务是成功还是失败。
  • 如果 Producer 仍然无法给出明确结果,Broker 会继续进行回查,直到达到一定重试上限后再采取默认处理(通常为回滚消息)。

时序图

Producer                           Broker
   |                                 |
   |---(1) Send half message-------> |  (消息先持久化, 状态=PREPARED)
   |<----------- Result ------------ |
   |                                 |
   |---(2) Do local transaction ---> |  (Producer 本地执行业务操作)
   |         (DB / RPC ... )        |
   |                                 |
   |---(3) Commit/Rollback---------> |  (更新消息状态=COMMITED / ROLLBACK)
   |                                 |
(若超时或 Producer 异常导致状态不明确, Broker 发起回查)
   |<------- Check message---------- |
   |---(回查逻辑: 再次确认本地事务)-- |

2. 典型使用场景

  1. 订单与支付:在用户支付完成后,需要保证订单状态与支付流水都一致更新,否则容易出现“支付成功但订单未更新”或“订单已完成但支付失败”等不一致状况。
  2. 库存扣减与订单创建:先发送“预扣库存消息”,在本地操作中扣减库存成功后,再提交消息通知后续流程;若扣减失败,则回滚。
  3. 积分发放:在用户下单成功后,需发放积分。在保证下单成功后再提交消息,保证不会出现“订单没成功却发放了积分”的情况。

总的来说,适合需要在不同系统之间协调操作结果,而且必须保证操作与消息发送的原子性的场景。


3. 事务消息的使用流程

3.1 Producer 侧

(1)创建 TransactionMQProducer

使用 Java 客户端时,我们需要实例化一个 TransactionMQProducer,并实现回调方法,用于处理本地事务状态的回查:

// 1. 创建 TransactionMQProducer
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group");
producer.setNamesrvAddr("127.0.0.1:9876");

// 2. 实现 TransactionListener
TransactionListener transactionListener = new TransactionListener() {
   
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
   
        // 在这里执行本地事务,例如数据库操作
        // arg 可传入业务参数
        try {
   
            // TODO: 执行业务操作, 如果成功:
            return LocalTransactionState.COMMIT_MESSAGE;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十方来财

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

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

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

打赏作者

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

抵扣说明:

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

余额充值