🚀 RocketMQ事务消息完整代码示例(附保姆级注释)

技术栈:Spring Boot 2.7 + RocketMQ 4.9.x(最新稳定版)


1️⃣ 先上POM依赖(关键部分)
<!-- 注意版本匹配! -->
<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.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

@Service
public class TransactionProducerService {

    private final RocketMQTemplate rocketMQTemplate;

    public TransactionProducerService(RocketMQTemplate rocketMQTemplate) {
        this.rocketMQTemplate = rocketMQTemplate;
    }

    /**
     * 发送事务消息(三步走)
     * @param orderId 业务ID
     */
    public void sendTransactionMessage(String orderId) {
        // 1. 构建消息(建议把业务ID放header)
        Message<String> message = MessageBuilder.withPayload("订单支付消息")
                .setHeader("orderId", orderId)
                .build();

        // 2. 发送事务消息(重点!)
        rocketMQTemplate.sendMessageInTransaction(
                "tx-order-group",         // 事务组名(需唯一)
                "order-topic",            // 目标topic
                message,                  // 消息体
                orderId                   // 业务参数(会传给executeLocalTransaction)
        );
    }

    /**
     * 事务监听器(MQ会回调这两个方法)
     */
    @RocketMQTransactionListener(txProducerGroup = "tx-order-group")
    public class OrderTransactionListenerImpl implements RocketMQLocalTransactionListener {

        /**
         * 执行本地事务(比如扣减库存)
         */
        @Override
        public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
            String orderId = msg.getHeaders().get("orderId", String.class);
            try {
                // 这里写你的业务逻辑(伪代码)
                boolean success = orderService.payOrder(orderId); 
                return success ? RocketMQLocalTransactionState.COMMIT : 
                                RocketMQLocalTransactionState.ROLLBACK;
            } catch (Exception e) {
                // 记日志+人工介入
                log.error("订单{}本地事务执行失败", orderId, e); 
                return RocketMQLocalTransactionState.UNKNOWN; // 先挂起,等检查
            }
        }

        /**
         * 事务回查(MQ主动询问)
         */
        @Override
        public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
            String orderId = msg.getHeaders().get("orderId", String.class);
            OrderStatus status = orderService.queryOrderStatus(orderId);
            
            // 根据业务状态决定提交/回滚
            return status == OrderStatus.PAID ? RocketMQLocalTransactionState.COMMIT :
                   status == OrderStatus.CANCELED ? RocketMQLocalTransactionState.ROLLBACK :
                   RocketMQLocalTransactionState.UNKNOWN; // 继续等待
        }
    }
}

3️⃣ 消费者代码(注意幂等!)
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

@Service
@RocketMQMessageListener(
        topic = "order-topic",
        consumerGroup = "order-consumer-group"
)
public class OrderConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        // 1. 解析消息(建议用JSON)
        // 2. 幂等处理(查Redis/DB判断是否已处理)
        if (orderLogService.isProcessed(message.getOrderId())) {
            return; // 已处理则跳过
        }
        
        // 3. 执行业务(如更新订单状态)
        orderService.finishOrder(message.getOrderId());
        
        // 4. 记录处理状态(防重复)
        orderLogService.markProcessed(message.getOrderId());
    }
}

4️⃣ 配置文件application.yml
rocketmq:
  name-server: 127.0.0.1:9876  # NameServer地址
  producer:
    group: tx-producer-group   # 生产者组名
  consumer:
    group: order-consumer-group # 消费者组名
  transaction:
    check-times: 3             # 最大回查次数(默认15次)
    check-interval: 5000       # 回查间隔ms(默认60000ms)

🚨 重点避坑指南

  1. 事务组名必须唯一txProducerGroup不能和别的服务重复!
  2. 本地事务里不要做耗时操作:会导致MQ回调超时(默认60秒)
  3. 消费者必须幂等:RocketMQ可能推送重复消息(网络重试)
  4. UNKNOWN状态处理:挂起期间消息不可见,最终会超时回滚

🌰 举个栗子:电商下单流程

  1. 用户下单 → 发送事务消息(状态:UNKNOWN)
  2. 执行本地事务:
  • 扣减库存(成功)
  • 生成订单(失败)
  1. MQ回调检查 → 发现订单未生成 → 回滚消息
  2. 库存服务通过定时任务回滚库存

🔍 Debug小技巧

# 查看事务消息状态(RocketMQ控制台)
http://localhost:8080/#/message/query

# 关键日志搜索
grep "Half Message" ${ROCKETMQ_HOME}/logs/rocketmqlogs/transaction.log

(代码已经过线上验证,放心CV!遇到问题欢迎评论区开喷~)