事务消息实现

梳理出一份清晰、可操作的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的事务消息功能!如果你对特定环节有更多疑问,我们可以继续探讨。

相关推荐
这个DBA有点耶12 小时前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G13 小时前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
自由能燃气设备13 小时前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远13 小时前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程
2601_9622186114 小时前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
张洛闻Eren15 小时前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
于平安15 小时前
MySQL-触发器
数据库·mysql
白远山15 小时前
上海24小时自助健身房解决方案实战指南与经验分享
java·数据库·架构·需求分析
Omics Pro15 小时前
斯坦福Nature+Science|广义虚拟细胞基础大模型
数据库·人工智能·算法·机器学习·自然语言处理
2603_9651481116 小时前
AI+API选品:下一代智能商务助手雏形已现
java·大数据·数据库·人工智能·数据挖掘