事务消息实现

梳理出一份清晰、可操作的RocketMQ事务消息实现指南。下面这个表格汇总了实现事务消息的四个核心组件及其职责,可以帮助你快速建立整体认知。

核心组件 主要职责 关键配置/方法
事务消息生产者 发送半消息,并触发本地事务执行 TransactionMQProducer, sendMessageInTransaction
事务监听器 核心:执行本地事务(如数据库操作)和响应Broker的状态回查 TransactionListener接口,实现executeLocalTransactioncheckLocalTransaction方法
消息消费者 订阅并处理已提交的事务消息 DefaultMQPushConsumer, 需实现幂等性逻辑
Spring Boot集成 通过注解和模板简化配置 @RocketMQTransactionListener, RocketMQTemplate

🔧 核心实现步骤

1. 添加依赖

首先,在你的Maven项目中引入RocketMQ的Spring Boot Starter依赖。

复制代码
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.2.3</version> <!-- 请使用最新稳定版本 -->
</dependency>
2. 配置生产者与事务监听器(核心)

这是实现事务消息的关键。你需要配置一个事务生产者,并为其绑定一个事务监听器。

复制代码
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;

@RocketMQTransactionListener(txProducerGroup = "tx-order-group") // 与此生产者组绑定
public class OrderTransactionListenerImpl implements RocketMQLocalTransactionListener {

    /**
     * 执行本地事务
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        try {
            // 1. 解析消息,获取业务数据
            String orderId = (String) msg.getHeaders().get("orderId");
            // 2. 执行本地业务逻辑,例如:创建订单、扣减库存
            boolean isSuccess = orderService.createOrder(orderId);
            
            // 3. 根据本地事务执行结果返回状态
            if (isSuccess) {
                // 本地事务成功,通知Broker提交消息
                return RocketMQLocalTransactionState.COMMIT;
            } else {
                // 本地事务失败,通知Broker回滚消息
                return RocketMQLocalTransactionState.ROLLBACK;
            }
        } catch (Exception e) {
            // 记录日志并返回UNKNOWN,触发后续的事务状态回查
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    }

    /**
     * Broker事务状态回查(兜底机制)
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        String orderId = (String) msg.getHeaders().get("orderId");
        // 查询本地事务的最终状态(例如,检查订单在数据库中的最终状态)
        OrderStatus status = orderService.queryOrderStatus(orderId);
        
        switch (status) {
            case PAID: // 业务已成功
                return RocketMQLocalTransactionState.COMMIT;
            case CANCELLED: // 业务已失败
                return RocketMQLocalTransactionState.ROLLBACK;
            default: // 状态仍不明确,继续等待下次回查
                return RocketMQLocalTransactionState.UNKNOWN;
        }
    }
}

事务监听器的两个方法至关重要

  • executeLocalTransaction :生产者发送半消息后,Broker会回调此方法 以执行本地事务(如订单入库)。此时消息对消费者不可见

  • checkLocalTransaction :如果Broker长时间未收到生产者返回的最终状态(COMMIT/ROLLBACK),它会主动回查此方法,以确认本地事务的最终结果,防止因网络闪断导致数据不一致。

3. 发送事务消息

在Service层,使用 RocketMQTemplate发送事务消息。

复制代码
@Service
public class OrderService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    public void createOrder(String orderId) {
        // 1. 构建消息
        Message<String> message = MessageBuilder.withPayload("订单支付消息")
                .setHeader("orderId", orderId) // 设置业务ID,用于回查时定位
                .build();
        // 2. 发送事务消息
        rocketMQTemplate.sendMessageInTransaction(
                "tx-order-group", // 事务生产者组名,需与@RocketMQTransactionListener中定义的一致
                "order-topic",    // 目标Topic
                message,          // 消息体
                orderId           // 业务参数,会传递给executeLocalTransaction方法的arg参数
        );
    }
}
4. 消费消息(消费者端)

消费者需要实现幂等性,以应对可能出现的重复消息。

复制代码
@Service
@RocketMQMessageListener(topic = "order-topic", consumerGroup = "order-consumer-group")
public class OrderConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        // 1. 解析消息
        // 2. 【关键】幂等性校验:通过订单ID等唯一键判断消息是否已被处理过
        if (orderLogService.isProcessed(message.getOrderId())) {
            return; // 已处理则直接跳过
        }
        // 3. 处理业务(例如,通知发货)
        orderService.notifyDelivery(message.getOrderId());
        // 4. 记录处理状态,防止重复消费
        orderLogService.markProcessed(message.getOrderId());
    }
}

⚠️ 关键注意事项

  1. 幂等性至关重要 :由于网络重试或Broker的回查机制,消费者可能收到重复消息 。必须在消费端通过唯一业务ID(如订单号)进行幂等校验,确保业务逻辑即使被重复执行也不会产生错误结果。

  2. 避免耗时操作executeLocalTransaction方法中不要进行耗时过长的操作,因为Broker有超时机制。如果长时间不返回结果,Broker会发起回查。

  3. UNKNOWN状态的处理 :当本地事务执行结果不确定时,返回 UNKNOWN是合理的。Broker会定期回查,直到获得明确状态(COMMIT/ROLLBACK)。但应避免持续返回UNKNOWN,需设置合理的回查策略。

  4. 资源清理 :如果本地事务执行失败并返回ROLLBACK,记得在本地进行相应的数据回滚或清理操作。

💡 事务消息流程回顾

简单回顾一下整个流程,帮助你加深理解:

  1. 发送半消息 :生产者发送消息,但此消息状态为Prepared,对消费者不可见

  2. 执行本地事务 :Broker回调生产者的 executeLocalTransaction方法执行本地业务。

  3. 提交/回滚 :生产者根据本地事务成功与否,向Broker发送COMMIT(消息对消费者可见)或ROLLBACK(消息被删除)指令。

  4. 状态回查(兜底) :如果Broker未收到最终指令,会定期调用 checkLocalTransaction方法来确认最终状态。

希望这份详细的代码示例和解释能帮助你顺利实现RocketMQ的事务消息功能!如果你对特定环节有更多疑问,我们可以继续探讨。

相关推荐
ChineHe38 分钟前
Redis基础篇004_Redis Pipeline流水线详解
数据库·redis·缓存
西柚补习生1 小时前
通用 PWM 原理基础教学
数据库·mongodb
小张程序人生1 小时前
ShardingJDBC读写分离详解与实战
数据库
木风小助理1 小时前
三大删除命令:MySQL 核心用法解析
数据库·oracle
tc&1 小时前
redis_cmd 内置防注入功能的原理与验证
数据库·redis·bootstrap
麦聪聊数据1 小时前
MySQL 性能调优:从EXPLAIN到JSON索引优化
数据库·sql·mysql·安全·json
Facechat2 小时前
视频混剪-时间轴设计
java·数据库·缓存
lalala_lulu2 小时前
MySQL中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别?(超详细版)
数据库·mysql
曹牧2 小时前
Oracle:大量数据删除
数据库·oracle
小四的快乐生活2 小时前
大数据SQL诊断(采集、分析、优化方案)
大数据·数据库·sql