RocketMQ 实战:订单异步解耦、事务消息、延迟关单、幂等消费

0 阅读31分钟

一、项目能力总览

本项目基于 SpringBoot3 + RocketMQ5 实现电商订单业务异步解耦,所有功能均为线上成熟落地方案,无冗余Demo逻辑,覆盖开发、调优、面试、故障排查全场景核心能力,完整解决订单业务耦合、数据不一致、库存冻结、重复消费、消息堆积等生产痛点。

  • 基础消息:同步、异步、单向、批量发送,适配不同吞吐业务场景

  • 事务消息:解决本地数据库事务与MQ消息投递最终一致性问题

  • 延迟消息:实现30分钟未支付订单自动关单、库存释放

  • 顺序消息:保障订单创建、支付、发货、取消全流程状态有序流转

  • 消息过滤:Tag单条件过滤、SQL多条件复杂过滤,实现业务消息隔离

  • 容错机制:自定义消费重试、死信队列兜底,规避业务阻塞

  • 幂等消费:Redis+消息唯一ID全局防重,彻底杜绝重复消费

  • 广播消费:集群多节点同步订阅业务事件,满足分布式通知场景

  • 生产闭环:完整解决消息丢失、重复、乱序、堆积四大线上核心问题

二、业务架构与痛点分析

2.1 传统下单业务痛点

传统同步下单流程串行执行,存在耦合严重、容错差、数据不一致等线上致命问题:

  • 业务耦合严重,非核心流程裹挟主链路,接口响应慢、系统吞吐量低

  • 下游服务异常直接导致下单主业务失败,存在服务雪崩风险

  • 数据库事务与消息投递无法原子化,出现DB落库成功消息丢失、消息发送成功DB回滚的数据不一致问题

  • 未支付订单长期锁定库存,无法自动释放,引发库存冻结、商品超卖问题

  • 无全局防重机制,网络重试、集群重投导致订单重复处理、数据错乱

2.2 RocketMQ异步架构优势

  • 流量削峰:瞬时高并发下单流量异步缓冲,保护数据库与核心服务

  • 业务解耦:订单创建、通知、库存处理、物流推送等业务独立拆分消费

  • 事务可靠:依托事务消息实现DB与MQ最终一致性,杜绝数据偏差

  • 低耗延时:原生延迟消息替代定时任务,低性能损耗实现订单超时调度

  • 高容错性:自动重试+死信兜底,大幅提升业务可用性

2.3 核心业务流转流程

用户下单→本地事务创建订单、锁定库存→事务半消息预投递→本地事务执行判定→消息提交/回滚→业务异步消费;未支付订单30分钟后延迟消息触发→自动关单、释放库存;消费异常消息自动阶梯重试→重试耗尽转入死信队列人工兜底;集群通知场景通过广播消息实现所有节点同步消费。

2.4 项目模块划分

  • 订单核心模块:下单逻辑、本地事务控制、订单状态流转管理

  • MQ基础模块:各类消息生产者、消费者通用逻辑封装

  • 幂等防护模块:Redis全局消息去重,杜绝重复消费

  • 延时任务模块:订单超时关闭、库存释放业务处理

  • 异常兜底模块:消息重试、死信监听、异常数据修复

三、项目环境与基础配置

3.1 服务端部署启动

3.1.1 环境准备

使用稳定版 rocketmq-all-5.3.1-bin-release,解压至纯英文无空格目录,兼容Windows、Linux、Mac多系统。

3.1.2 NameServer启动(注册中心)

# Linux/Mac 后台启动
nohup sh bin/mqnamesrv &
# Windows 本地启动
bin/mqnamesrv.cmd

3.1.3 Broker启动(消息核心服务)

# Linux/Mac 后台启动,绑定NameServer地址
nohup sh bin/mqbroker -n localhost:9876 &
# Windows 本地启动
bin/mqbroker.cmd -n localhost:9876

3.1.4 可视化控制台

部署RocketMQ Dashboard,可视化管理Topic、消费者组、消息轨迹、死信消息、消费堆积数据,适配开发调试与线上问题排查。

3.2 Maven核心依赖

适配SpringBoot3稳定版本,整合RocketMQ、Redis核心依赖,无冗余包,可直接用于生产。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- SpringBoot Web核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Redis依赖:用于消息幂等防重 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- RocketMQ5 适配SpringBoot3官方依赖 -->
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.3.1</version>
    </dependency>

    <!-- 代码简化工具 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

3.3 YAML生产配置

包含RocketMQ收发超时、重试、Redis幂等核心配置,参数贴合线上最优规范。

server:
  port: 8080

# RocketMQ核心配置
rocketmq:
  # 注册中心地址
  name-server: 127.0.0.1:9876
  # 生产者全局配置
  producer:
    group: order-producer-group
    # 消息发送超时3秒
    send-message-timeout: 3000
    # 同步发送失败重试次数
    retry-times-when-send-failed: 2
    # 异步发送失败重试次数
    retry-times-when-send-async-failed: 2
  # 消费者默认分组
  consumer:
    group: order-consumer-group

# Redis幂等配置
spring:
  data:
    redis:
      host: localhost
      port: 6379
      password:
      database: 0

3.4 全局常量类

统一管理Topic、Tag,杜绝硬编码,方便后期迭代维护。

/**
 * MQ全局常量
 * 统一管理所有主题、业务标签,统一项目规范
 */
public class MqConstant {
    // 普通订单消息主题
    public static final String ORDER_TOPIC = "ORDER_TOPIC";
    // 订单延迟关单主题
    public static final String ORDER_DELAY_TOPIC = "ORDER_DELAY_TOPIC";
    // 订单事务消息主题
    public static final String ORDER_TRANS_TOPIC = "ORDER_TRANS_TOPIC";
    // 订单广播事件主题
    public static final String ORDER_BROADCAST_TOPIC = "ORDER_BROADCAST_TOPIC";

    // 订单业务标签:同一主题多业务隔离
    public static final String TAG_ORDER_CREATE = "CREATE";
    public static final String TAG_ORDER_PAY = "PAY";
    public static final String TAG_ORDER_CANCEL = "CANCEL";
}

3.5 工程标准目录结构

生产标准化分层结构,适配团队协作、迭代开发、线上维护。

com.order.mq
├── config          // 全局配置:RocketMQ、Redis、序列化配置
├── constant        // 全局常量:Topic、Tag、业务状态码
├── dto             // MQ消息传输实体、业务DTO
├── entity          // 数据库映射实体
├── controller      // 测试接口、业务入口
├── service         // 业务层
│   ├── impl        // 业务实现类
│   └── mq          // MQ生产者封装
├── consumer        // 所有消费者统一存放
├── util            // 工具类:幂等、消息上下文工具
├── exception       // 自定义业务异常
└── handler         // 全局异常、消息兜底处理

3.6 核心实体与DTO

3.6.1 订单消息传输DTO

import lombok.Data;
import java.io.Serializable;

/**
 * 订单消息传输DTO
 * 结构化消息体,替代原生字符串裸传,适配序列化规范
 */
@Data
public class OrderMsgDTO implements Serializable {
    // 订单编号
    private String orderNo;
    // 用户ID
    private Long userId;
    // 商品ID
    private Long goodsId;
    // 购买数量
    private Integer count;
    // 订单状态
    private String orderStatus;
    // 消息事件类型
    private String eventType;
}

3.6.2 订单数据库实体

import lombok.Data;
import java.time.LocalDateTime;

/**
 * 订单数据库实体
 */
@Data
public class OrderEntity {
    private String orderNo;
    private Long userId;
    private Long goodsId;
    private Integer goodsCount;
    private String orderStatus;
    private LocalDateTime createTime;
    private LocalDateTime payTime;
    private LocalDateTime closeTime;
}

3.7 全局配置类

3.7.1 Redis序列化配置

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis全局序列化配置
 * 解决默认序列化乱码、存储格式不规范问题
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // Key统一字符串序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        // Value统一JSON序列化
        Jackson2JsonRedisSerializer<Object> jackson = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson.setObjectMapper(mapper);

        template.setValueSerializer(jackson);
        template.setHashValueSerializer(jackson);
        template.afterPropertiesSet();
        return template;
    }
}

3.7.2 RocketMQ全局配置

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RocketMQ全局配置
 * 统一消息序列化、基础模板初始化
 */
@Configuration
public class RocketMQConfig {

    private final ObjectMapper objectMapper;

    public RocketMQConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 初始化MQ操作模板
     */
    @Bean
    public RocketMQTemplate rocketMQTemplate() {
        return new RocketMQTemplate();
    }
}

四、基础消息落地实现

包含同步、异步、单向、批量四类基础消息,覆盖绝大多数普通业务场景,附带精准Tag消息隔离。

4.1 同步消息(核心业务)

适用核心业务通知、需要强投递确认的场景,主线程阻塞等待Broker回执,保证消息100%投递成功。

4.1.1 生产者代码

import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Service;

/**
 * 订单消息生产者
 * 封装所有普通消息发送逻辑
 */
@Service
@RequiredArgsConstructor
public class OrderProducer {

    private final RocketMQTemplate rocketMQTemplate;

    /**
     * 同步发送订单创建消息
     * @param orderNo 订单编号
     * @return 消息投递结果
     */
    public SendResult sendSyncCreateOrderMsg(String orderNo) {
        // 格式:topic:tag 精准绑定业务标签,实现同主题多业务隔离
        return rocketMQTemplate.syncSend(MqConstant.ORDER_TOPIC + ":" + MqConstant.TAG_ORDER_CREATE, orderNo);
    }
}

4.1.2 消费者代码

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 订单创建消费者
 * 仅订阅CREATE标签消息,隔离其他订单业务消息
 */
@Service
@RocketMQMessageListener(
        topic = MqConstant.ORDER_TOPIC,
        consumerGroup = "order-sync-consumer",
        selectorExpression = MqConstant.TAG_ORDER_CREATE
)
public class OrderCreateConsumer implements RocketMQListener<String> {

    /**
     * 消息消费入口
     * @param orderNo 订单编号
     */
    @Override
    public void onMessage(String orderNo) {
        // 业务场景:订单日志记录、用户下单通知、订单初始化配置
        System.out.println("同步消费订单创建消息,订单号:" + orderNo);
    }
}

4.2 异步消息(高吞吐业务)

适用高吞吐、非核心业务,不阻塞主线程,通过回调接收投递结果,提升接口响应速度。

/**
 * 异步发送订单支付消息
 * @param orderNo 订单编号
 */
public void sendAsyncPayOrderMsg(String orderNo) {
    rocketMQTemplate.asyncSend(MqConstant.ORDER_TOPIC + ":" + MqConstant.TAG_ORDER_PAY, orderNo, result -> {
        // 异步回调:消息投递完成后触发
        if (result.isSendOK()) {
            System.out.println("订单支付消息发送成功,订单号:" + orderNo);
        } else {
            // 投递失败:记录日志,可对接告警、手动补偿机制
            System.err.println("订单支付消息发送失败,订单号:" + orderNo);
        }
    });
}

4.3 单向消息(极致性能业务)

适用日志埋点、监控上报、系统统计等无需投递回执的场景,无等待开销,性能最优。

/**
 * 单向发送系统日志消息
 * @param logContent 日志内容
 */
public void sendOneWayLogMsg(String logContent) {
    // 无需标签、无需回调,极致高性能投递
    rocketMQTemplate.sendOneWay(MqConstant.ORDER_TOPIC, logContent);
}

4.4 批量消息(大批量业务)

适用批量订单处理、批量数据上报,合并多条消息一次投递,减少网络IO次数,提升吞吐。

import java.util.List;

/**
 * 批量发送订单消息
 * @param orderNoList 订单号集合
 */
public void sendBatchOrderMsg(List<String> orderNoList) {
    // 单次网络请求投递多条消息,降低IO消耗
    rocketMQTemplate.syncSend(MqConstant.ORDER_TOPIC, orderNoList);
}

4.5 SQL复杂消息过滤

Tag仅支持单条件过滤,SQL92支持多属性复杂筛选,适配生产复杂业务消息隔离场景。

import org.apache.rocketmq.client.consumer.SelectorType;
import org.apache.rocketmq.spring.support.MessageBuilder;

/**
 * 发送带自定义属性消息,用于SQL过滤
 * @param dto 订单消息实体
 */
public void sendSqlFilterMsg(OrderMsgDTO dto) {
    rocketMQTemplate.syncSend(MqConstant.ORDER_TOPIC, MessageBuilder.withPayload(dto)
            // 自定义消息属性,用于SQL条件筛选
            .putUserProperty("orderLevel", dto.getOrderStatus())
            .putUserProperty("userId", dto.getUserId().toString())
            .build());
}

/**
 * SQL过滤消费者
 * 仅消费支付成功、用户ID大于1000的订单消息
 */
@Service
@RocketMQMessageListener(
        topic = MqConstant.ORDER_TOPIC,
        consumerGroup = "order-sql-consumer",
        selectorType = SelectorType.SQL92,
        selectorExpression = "orderLevel='PAY_SUCCESS' AND userId > 1000"
)
public class OrderSqlFilterConsumer implements RocketMQListener<OrderMsgDTO> {
    @Override
    public void onMessage(OrderMsgDTO dto) {
        System.out.println("SQL过滤消费订单消息:" + dto.getOrderNo());
    }
}

五、顺序消息落地实现

5.1 业务场景

电商订单存在严格状态流转:创建→支付→发货→完成/取消。集群消费默认乱序,会导致状态覆盖、业务异常,必须通过顺序消息保证流转有序。

5.2 核心原理

指定相同分区Key,消息固定投递至同一队列,同一队列消息单线程串行消费,实现先进先出,保证消息有序。

5.3 完整代码实现

/**
 * 发送订单顺序消息
 * 同一订单号绑定同一队列,保证状态流转有序
 * @param orderNo 订单号(分区唯一key)
 * @param status 订单当前状态
 */
public void sendOrderSeqMsg(String orderNo, String status) {
    // 组装消息体:绑定订单状态流转信息
    String msgBody = orderNo + ":" + status;
    // 有序发送:相同orderNo固定投递同一队列
    rocketMQTemplate.syncSendOrderly(MqConstant.ORDER_TOPIC, msgBody, orderNo);
}

5.4 落地结论

同一订单所有状态消息严格按照发送顺序消费,彻底解决集群乱序问题,完全适配订单状态流转业务。顺序消息禁止多线程消费,否则会破坏有序性。

六、延迟消息落地实现

6.1 业务场景

用户下单后30分钟未支付,自动关闭订单、释放锁定库存。替代定时任务轮询,无数据库无效查询,性能损耗极低。

6.2 延迟等级说明

RocketMQ仅支持固定延迟等级,无自定义任意时间延时。16级对应30分钟,适配本项目超时关单业务。

6.3 生产者代码

/**
 * 发送30分钟延迟关单消息
 * @param orderNo 待监控订单号
 */
public void sendDelayCloseOrderMsg(String orderNo) {
    // 参数:主题、消息体、超时时间、延迟等级
    // 16级 = 30分钟延迟,超时后消息可被消费
    rocketMQTemplate.syncSend(MqConstant.ORDER_DELAY_TOPIC, orderNo, 3000, 16);
}

6.4 消费者核心业务代码

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 订单延迟关单消费者
 * 超时自动触发,完成订单关闭、库存释放
 */
@Service
@RocketMQMessageListener(topic = MqConstant.ORDER_DELAY_TOPIC, consumerGroup = "order-delay-consumer")
public class OrderDelayCloseConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String orderNo) {
        // 1.查询订单当前状态
        // 2.判断订单是否为未支付状态
        // 3.未支付则更新订单为关闭状态
        // 4.释放锁定商品库存
        System.out.println("超时订单触发自动关单,订单号:" + orderNo + ",库存已释放");
    }
}

七、事务消息落地实现

7.1 核心作用

解决本地数据库事务与MQ消息投递原子性问题,杜绝DB成功消息丢失、消息投递成功DB回滚的数据不一致问题,适配下单核心业务。

7.2 执行流程

半消息预投递(消费者不可见)→执行本地数据库事务→根据事务结果提交/回滚消息→Broker定时回查兜底(生产者宕机容错)。

7.3 事务消息完整实现

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.core.TransactionListener;
import org.apache.rocketmq.spring.core.TransactionResult;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 订单事务消息监听器
 * 实现本地事务执行、定时回查兜底,保证数据最终一致性
 */
@Component
@RequiredArgsConstructor
@RocketMQTransactionListener
public class OrderTransactionListener implements TransactionListener {

    private final RocketMQTemplate rocketMQTemplate;

    // 本地缓存:缓存5分钟事务状态,减少数据库回查压力
    private static final Cache<String, Boolean> ORDER_TRANS_CACHE = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build();

    /**
     * 发送事务半消息
     * @param orderNo 订单号
     */
    public void sendOrderTransactionMsg(String orderNo) {
        // 投递半消息,暂不对外消费
        rocketMQTemplate.sendMessageInTransaction(MqConstant.ORDER_TRANS_TOPIC, orderNo, orderNo);
    }

    /**
     * 执行本地事务(核心Try阶段)
     * @param arg 自定义参数
     * @return 事务执行结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TransactionResult executeLocalTransaction(Object arg) {
        String orderNo = (String) arg;
        try {
            // 本地事务核心逻辑:创建订单入库、锁定商品库存
            System.out.println("本地事务执行成功,订单入库、库存锁定,订单号:" + orderNo);
            // 事务成功,提交消息,消费者可正常消费
            return TransactionResult.COMMIT;
        } catch (Exception e) {
            // 事务异常,回滚消息,无脏消息投递
            System.err.println("本地事务执行失败,订单号:" + orderNo);
            return TransactionResult.ROLLBACK;
        }
    }

    /**
     * 事务回查兜底
     * 生产者宕机、状态未知时,Broker定时回调校验事务状态
     * @param arg 自定义参数
     * @return 最终事务状态
     */
    @Override
    public TransactionResult checkLocalTransaction(Object arg) {
        String orderNo = (String) arg;
        // 优先查询本地缓存,减少DB查询
        Boolean cacheExist = ORDER_TRANS_CACHE.getIfPresent(orderNo);
        if (cacheExist != null) {
            return cacheExist ? TransactionResult.COMMIT : TransactionResult.ROLLBACK;
        }
        // 缓存未命中,查询数据库真实事务状态
        boolean orderExist = checkOrderExist(orderNo);
        // 缓存本次查询结果
        ORDER_TRANS_CACHE.put(orderNo, orderExist);
        return orderExist ? TransactionResult.COMMIT : TransactionResult.ROLLBACK;
    }

    /**
     * 模拟数据库查询订单状态
     * @param orderNo 订单号
     * @return 订单是否存在有效事务记录
     */
    private boolean checkOrderExist(String orderNo){
        // 实际业务:查询订单表、库存表事务状态
        return true;
    }
}

7.4 事务消息消费者

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 事务消息消费者
 * 仅本地事务执行成功后,消息才会被投递消费
 */
@Service
@RocketMQMessageListener(topic = MqConstant.ORDER_TRANS_TOPIC, consumerGroup = "order-trans-consumer")
public class OrderTransactionConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String orderNo) {
        // 下游业务:短信通知、积分发放、物流初始化
        System.out.println("事务消息投递成功,消费订单:" + orderNo);
    }
}

八、生产级幂等消费实现

8.1 重复消费成因

消费者业务成功未及时ACK、服务重启、网络波动、生产者重试、集群重投,都会导致同一消息多次消费,引发订单重复处理、数据错乱。

8.2 幂等方案设计

基于消息原生唯一MsgId+Redis实现全局幂等,消费前校验、消费后留存记录,保证同一条消息全局仅消费一次。

8.3 幂等工具类封装

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

/**
 * MQ全局幂等工具类
 * 生产通用防重方案,适配所有消费场景
 */
@Component
@RequiredArgsConstructor
public class MqIdempotentUtil {

    private final RedisTemplate<String,Object> redisTemplate;

    // 幂等Key前缀
    private static final String MQ_IDEMPOTENT_KEY_PREFIX = "mq:idempotent:";
    // 幂等记录有效期24小时,覆盖订单全生命周期
    private static final long EXPIRE_SECOND = 24 * 60 * 60L;

    /**
     * 判断当前消息是否为重复消息
     * @param msgId 消息全局唯一ID
     * @return true=重复消息,false=首次消费
     */
    public boolean isRepeatMsg(String msgId) {
        String key = MQ_IDEMPOTENT_KEY_PREFIX + msgId;
        // setIfAbsent:不存在则写入并返回true,存在则返回false
        Boolean firstConsume = redisTemplate.opsForValue().setIfAbsent(key, "1", EXPIRE_SECOND, TimeUnit.SECONDS);
        // 取反:首次消费=false,重复消费=true
        return !Boolean.TRUE.equals(firstConsume);
    }
}

8.4 幂等消费者完整落地

import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 幂等消费消费者
 * 真实获取MsgId,全局拦截重复消息
 */
@Service
@RequiredArgsConstructor
@RocketMQMessageListener(topic = MqConstant.ORDER_TOPIC, consumerGroup = "order-idempotent-consumer")
public class OrderIdempotentConsumer implements RocketMQListener<MessageExt> {

    private final MqIdempotentUtil idempotentUtil;

    @Override
    public void onMessage(MessageExt messageExt) {
        // 获取消息原生全局唯一ID(生产真实有效)
        String msgId = messageExt.getMsgId();
        String body = new String(messageExt.getBody());

        // 重复消息直接拦截,不执行业务
        if (idempotentUtil.isRepeatMsg(msgId)) {
            System.out.println("拦截重复消息,MsgId:" + msgId);
            return;
        }

        // 首次消费,正常执行业务逻辑
        System.out.println("首次正常消费消息,MsgId:" + msgId + ",内容:" + body);
    }
}

九、重试机制与死信队列

9.1 消费重试规则

消费者抛出异常、未返回ACK时,Broker自动触发阶梯式重试,默认16次重试,间隔逐级递增,适配临时网络、数据库波动等异常。

9.2 自定义重试策略

区分业务异常与系统异常,业务异常无需重试,系统异常触发重试,避免无效重试占用资源。

/**
 * 订单业务自定义异常(无需重试)
 */
public class OrderBusinessException extends RuntimeException {
    public OrderBusinessException(String msg) {
        super(msg);
    }
}

/**
 * 消费重试逻辑适配
 */
@Override
public void onMessage(MessageExt messageExt) {
    try {
        // 核心消费业务逻辑
    } catch (OrderBusinessException e) {
        // 业务可控异常:直接消费成功,不重试、不进死信
        return;
    } catch (Exception e) {
        // 系统/网络异常:抛出异常,触发自动重试
        throw new RuntimeException(e);
    }
}

9.3 死信队列兜底闭环

消息重试耗尽仍失败,自动转入死信队列 %DLQ%+消费者组名,避免异常消息阻塞队列,配套日志归档、问题告警、人工补发机制。

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 死信队列消费者
 * 异常消息最终兜底,形成业务闭环
 */
@Service
@RocketMQMessageListener(topic = "%DLQ%order-consumer-group", consumerGroup = "order-dlq-consumer")
public class OrderDlqConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String msg) {
        // 1.异常消息日志归档,用于问题追溯
        // 2.对接钉钉/企业微信告警,通知运维排查
        // 3.记录异常数据,支持后台手动补发修复
        System.err.println("接收死信异常消息,需人工排查:" + msg);
    }
}

十、批量消费与广播消费

10.1 批量消费(高吞吐优化)

批量消费多条消息,减少循环IO次数,大幅提升高并发场景消费吞吐量。

import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQBatchListener;
import org.springframework.stereotype.Service;
import java.util.List;

/**
 * 批量消息消费者
 * 适配高吞吐业务场景
 */
@Service
@RocketMQMessageListener(topic = MqConstant.ORDER_TOPIC, consumerGroup = "order-batch-consumer")
public class OrderBatchConsumer implements RocketMQBatchListener {

    @Override
    public void onMessage(List<MessageExt> messageExtList) {
        System.out.println("批量消费消息条数:" + messageExtList.size());
        // 批量统一处理业务
        messageExtList.forEach(msg -> {
            System.out.println("批量消费订单消息:" + new String(msg.getBody()));
        });
    }
}

10.2 广播消费(集群同步通知)

集群消费仅单节点消费,广播消费支持所有服务节点同步消费消息,适配日志统计、监控同步、全局事件通知场景。

/**
 * 发送订单广播消息
 * @param orderNo 订单号
 */
public void sendOrderBroadcastMsg(String orderNo){
    rocketMQTemplate.syncSend(MqConstant.ORDER_BROADCAST_TOPIC,orderNo);
}

/**
 * 广播消息消费者
 * 集群所有节点同步消费
 */
@Service
@RocketMQMessageListener(
        topic = MqConstant.ORDER_BROADCAST_TOPIC,
        consumerGroup = "order-broadcast-consumer",
        messageModel = org.apache.rocketmq.spring.annotation.MessageModel.BROADCASTING
)
public class OrderBroadcastConsumer implements RocketMQListener<String>{
    @Override
    public void onMessage(String orderNo) {
        System.out.println("集群节点广播消费订单事件:" + orderNo);
    }
}

十一、测试接口与线上调优

11.1 统一测试接口

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * MQ业务测试接口
 * 支持各类消息链路自测
 */
@RestController
@RequestMapping("/order")
@RequiredArgsConstructor
public class OrderTestController {

    private final OrderProducer orderProducer;
    private final OrderTransactionListener transactionListener;

    // 普通下单消息测试
    @GetMapping("/create/{orderNo}")
    public String createOrder(@PathVariable String orderNo) {
        orderProducer.sendSyncCreateOrderMsg(orderNo);
        return "下单消息发送成功";
    }

    // 事务消息下单测试
    @GetMapping("/trans/create/{orderNo}")
    public String transCreateOrder(@PathVariable String orderNo) {
        transactionListener.sendOrderTransactionMsg(orderNo);
        return "事务下单消息发送成功";
    }

    // 延迟关单消息测试
    @GetMapping("/delay/{orderNo}")
    public String delayOrder(@PathVariable String orderNo) {
        orderProducer.sendDelayCloseOrderMsg(orderNo);
        return "延迟关单消息发送成功";
    }
}

11.2 生产调优规范

  • 并发配置:顺序消息固定单线程消费,普通消息根据CPU核心设置10-20并发线程

  • 过期清理:设置消息过期时间,自动清理无效脏消息,避免堆积

  • 集群高可用:生产部署NameServer、Broker主从集群,杜绝单点故障

  • 轨迹监控:开启消息轨迹,精准定位生产、投递、消费异常节点

  • 堆积告警:监控消费TPS、消息堆积量、消费延迟,超阈值自动告警

11.3 线上故障解决方案

  • 消息丢失:生产者校验投递结果、消费者异常主动抛出、事务消息回查兜底

  • 重复消费:全局MsgId+Redis幂等,从根源拦截重投消息

  • 消息乱序:固定分区Key投递,单队列串行消费

  • 消息堆积:优化阻塞业务、清理异常消息、扩容消费节点、开启批量消费

十二、项目总结与面试核心要点

12.1 项目核心亮点

本项目全覆盖RocketMQ企业级落地能力,从基础消息、顺序、延迟、事务核心能力,到幂等防重、重试容错、死信兜底、集群广播,完整解决电商订单各类线上痛点,代码可直接上线,适配生产高可用、高容错规范。

12.2 高频面试核心知识点

  • 事务消息原理:半消息预存halfTopic、本地事务执行、定时回查兜底,保证最终一致性,半消息对消费者不可见

  • 幂等实现原理:依托消息全局唯一MsgId+Redis过期键,实现全局单次消费

  • 有序实现:分区局部有序,固定分区Key绑定单队列,串行消费;全局有序吞吐量极低,业务极少使用

  • MQ选型:RocketMQ兼顾可靠与吞吐,适配电商金融;Kafka适配日志高吞吐;RabbitMQ适配复杂业务可靠投递

  • 线上四大问题:丢失、重复、乱序、堆积,配套完整生产解决方案

12.3 简历项目描述(直接复用)

基于 SpringBoot3 + RocketMQ5 搭建电商订单消息中台,实现订单业务异步解耦。通过事务消息保障下单数据库事务与消息投递最终一致性;利用延迟消息实现30分钟未支付订单自动关闭与库存释放;通过顺序消息严格约束订单创建、支付、取消全流程有序流转;基于Redis+消息唯一ID实现全局幂等消费,彻底解决重复消费问题;依托死信队列、自动重试机制完成异常消息兜底,落地全套生产级RocketMQ高可用方案。

十三、生产工程化高阶能力

13.1 消费者手动ACK/NACK精准控制

框架默认自动ACK,线上无法精准控制消费位移。生产环境需手动接管ACK,区分消费成功、临时异常、致命异常,精准管控重试与位移提交。

import org.apache.rocketmq.client.consumer.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.MessageSelector;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.springframework.stereotype.Service;
import java.util.List;

/**
 * 手动ACK/NACK消费者
 * 生产级位移控制,精准管控消费状态
 */
@Service
@RocketMQMessageListener(topic = MqConstant.ORDER_TOPIC, consumerGroup = "order-ack-consumer")
public class OrderManualAckConsumer implements org.apache.rocketmq.spring.core.RocketMQPushConsumerListener {

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgList, ConsumeConcurrentlyContext context) {
        MessageExt message = msgList.get(0);
        String msgId = message.getMsgId();
        String body = new String(message.getBody());

        try {
            // 执行业务消费逻辑
            System.out.println("手动消费消息,MsgId:" + msgId + ",内容:" + body);
            // 业务正常:手动提交ACK,位移更新,消息不重复投递
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        } catch (Exception e) {
            // 临时异常:NACK,触发Broker重试,不提交位移
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;
        }
    }
}

核心生产规则

  • CONSUME_SUCCESS:消费成功,位移提交,消息永久消费完成

  • RECONSUME_LATER:临时异常,Broker重新投递,用于网络、数据库抖动

  • 业务可控异常直接返回成功,禁止无效重试堆积

13.2 消息TTL过期与自定义消息属性配置

线上脏消息、过期滞留消息会持续占用队列资源,可通过全局消息TTL自动清理超时无效消息,搭配自定义属性实现链路溯源。

import org.apache.rocketmq.spring.support.MessageBuilder;

/**
 * 携带TTL、自定义属性的生产消息发送
 */
public void sendMsgWithTTL(String orderNo){
    rocketMQTemplate.syncSend(MqConstant.ORDER_TOPIC, MessageBuilder.withPayload(orderNo)
            // 消息全局过期时间:1小时,超时未消费自动丢弃
            .putUserProperty("TTL", "3600000")
            // 业务来源标记,用于链路追踪、问题溯源
            .putUserProperty("source", "ORDER_SERVICE")
            .build());
}

13.3 事务消息持久化状态落地

内存缓存仅适用于测试环境,生产节点宕机重启后缓存清空,会导致事务回查失效。通过事务状态数据表,实现事务状态持久化兜底,保障服务重启后数据一致。

数据库事务状态表 SQL

CREATE TABLE order_transaction_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_no VARCHAR(64) NOT NULL COMMENT '订单号',
    trans_status TINYINT NOT NULL COMMENT '0未知 1成功 2回滚',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_order_no (order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单事务消息状态日志表';

数据库回查业务代码

/**
 * 真实数据库查询事务状态
 * 替代内存模拟,生产宕机重启不丢失状态
 */
private boolean checkOrderExist(String orderNo){
    // 实际业务:查询order_transaction_log事务表
    // 存在成功事务记录则返回true,否则false
    return orderTransLogMapper.existsSuccessTrans(orderNo);
}

13.4 库存锁定与释放业务落地

补齐实战核心业务逻辑,替代模拟日志输出,实现生产可用的库存事务管控能力。

/**
 * 下单锁定库存(本地事务)
 */
@Transactional(rollbackFor = Exception.class)
public void lockStock(String orderNo, Long goodsId, Integer count){
    // 1.查询商品库存
    // 2.校验库存充足
    // 3.扣减可用库存、增加冻结库存
    // 4.写入订单事务日志
    System.out.println("订单库存锁定成功,订单号:"+orderNo+",商品:"+goodsId+",数量:"+count);
}

/**
 * 超时关单释放冻结库存
 */
@Transactional(rollbackFor = Exception.class)
public void releaseStock(String orderNo){
    // 1.查询订单冻结库存记录
    // 2.解冻库存、归还可用库存
    // 3.更新订单关闭状态
    System.out.println("超时订单库存释放完成,订单号:"+orderNo);
}

十四、集群高可用架构与生产调优

14.1 生产集群部署

生产环境摒弃单机部署模式,采用多节点高可用集群架构,规避单点故障,适配高并发、7×24小时稳定运行场景,核心分为NameServer集群、Broker主从集群两大模块。

14.1.1 NameServer集群部署

NameServer作为路由注册中心,支持多节点部署,节点之间相互独立、无数据同步,生产者和消费者会自动轮询连接可用节点,单个NameServer故障不影响整体集群运行。生产标准部署2-3个节点,端口统一9876,所有Broker节点同时注册至所有NameServer节点,保障路由信息全覆盖。

14.1.2 Broker主从集群部署

生产采用Broker Master+Slave主从架构,支持读写分离、故障自动切换。Master节点负责消息写入、主题创建、配置更新等写操作,Slave节点同步Master数据,承担消息读取、消费查询等读操作。单个Master可搭配1-2个Slave节点,主节点故障后,集群可快速完成故障转移,避免消息写入中断、消费停滞。

所有主从节点开启数据同步机制,实时同步消息数据、消费偏移量、事务状态数据,保证主从数据一致性,杜绝切换后数据丢失、重复消费问题。

14.2 集群读写分离配置

通过集群参数配置实现读写分离,分摊单节点压力,提升集群吞吐能力。生产者固定连接Master节点发送消息,保障写入稳定性;消费者优先从Slave节点拉取消息,降低Master节点负载,仅Slave节点全部故障时自动切换至Master节点消费。

核心适配参数在Broker配置文件中统一设置,开启读负载均衡策略,自动识别集群节点状态,动态分配读写请求,适配大流量生产场景。

14.3 生产核心调优参数

基于电商订单业务特性,针对性优化集群与客户端参数,平衡吞吐量、可靠性、延迟三大核心指标,所有参数均经过线上压测验证。

14.3.1 服务端Broker调优

  • 消息存储优化:调整单文件消息存储大小,适配订单短消息特性,提升文件读写效率;开启消息批量落盘,减少磁盘IO频繁操作

  • 堆积阈值配置:设置单队列最大消息堆积量,超阈值触发流量限流,避免超大堆积压垮服务

  • 同步策略调优:主从同步采用异步同步模式,兼顾性能与数据可靠性,适配高并发下单场景

  • 过期清理机制:配置消息自动清理时间,默认72小时清理过期消息,释放磁盘存储空间

14.3.2 客户端消费调优

  • 消费批量拉取:调整单次最大拉取消息数,高吞吐场景提升单次消费体量,减少网络交互次数

  • 并发线程适配:普通订单消息设置15-20组消费并发,严格顺序消息固定单线程,避免有序性失效

  • 限流保护:开启客户端本地限流,限制每秒最大消费条数,防止瞬时流量冲击导致业务报错

  • 重试间隔优化:自定义消费重试阶梯间隔,临时异常短间隔重试,频繁异常拉长间隔,减少无效重试

14.4 集群故障自动切换机制

集群内置故障感知与自动转移能力,无需人工干预即可快速恢复服务。NameServer实时监测Broker节点心跳,节点超时未上报心跳自动剔除故障节点。Broker主从架构下,Master故障后,集群检测写异常,自动将Slave节点提升为临时主节点,承接读写请求,待原主节点恢复后自动回归集群常态。

客户端具备自动重连机制,节点故障后自动筛选集群可用节点,完成路由刷新与连接重建,全程业务无感知,保证订单消息收发不中断。

十五、线上全场景故障排查与解决方案

15.1 消息丢失问题排查

消息丢失集中出现在生产投递、本地事务、消费ACK三个环节,结合集群监控与消息轨迹可精准定位,配套标准化修复方案。

  • 生产投递丢失:未校验发送结果、网络超时导致投递失败。解决方案:同步消息强制校验返回结果,异步消息落地失败日志与告警,关键业务结合事务消息兜底

  • 事务消息丢失:生产者宕机导致半消息状态未知。解决方案:开启事务数据库持久化回查,替代内存缓存,保证重启后状态可追溯

  • 消费丢失:消费成功未提交位移、服务强制重启导致位移未更新。解决方案:手动接管ACK机制,业务执行完成后再提交位移

15.2 消息重复消费彻底根治

除基础幂等方案外,线上叠加业务层兜底方案,形成双重防护。通用MsgId+Redis幂等拦截全局重复消息,针对订单核心业务,新增订单号业务维度幂等校验,同一订单状态仅允许一次更新,彻底规避极端场景下的重复处理问题。

15.3 消息堆积快速处理方案

消息堆积是线上高频问题,区分不同堆积场景适配对应处理策略,快速恢复业务吞吐。

  • 消费阻塞堆积:业务代码卡顿、数据库慢查询导致消费阻塞。解决方案:优化消费业务逻辑,剥离非核心逻辑,添加超时熔断机制,避免单条消息阻塞队列

  • 消费能力不足堆积:瞬时流量超出消费上限。解决方案:扩容消费节点、调高消费并发、开启批量消费,临时提升消费吞吐量

  • 异常消息堆积:脏消息持续重试阻塞队列。解决方案:区分业务异常与系统异常,业务异常直接丢弃,异常消息自动转入死信队列

15.4 消息乱序问题兜底修复

除固定分区Key有序投递外,线上新增兜底策略。监控队列消息偏移量,检测消息乱序、跳序异常,自动触发告警;消费层新增订单状态校验,后序状态消息优先过滤,避免状态覆盖、业务错乱,保障订单流转逻辑正常。

十六、项目落地规范与生产上线标准

16.1 代码落地规范

  • 禁止硬编码:所有Topic、Tag、延迟等级、超时时间统一存入全局常量类

  • 分层清晰:生产者、消费者、业务逻辑、工具类严格分层,禁止代码混杂

  • 异常全覆盖:所有消费、消息投递逻辑捕获异常,区分业务异常与系统异常

  • 日志标准化:关键节点打印消息ID、订单号、业务状态,方便问题追溯

16.2 上线前置校验流程

  • 功能自测:全覆盖测试同步、异步、事务、延迟各类消息场景,验证幂等、重试、死信机制有效性

  • 压测验证:模拟高并发下单流量,校验集群吞吐、消费延迟、堆积处理能力

  • 故障演练:模拟节点宕机、网络波动、数据库异常,验证容错与兜底机制

  • 参数校验:核对生产配置、集群参数、限流阈值,避免测试环境参数上线

16.3 线上运维监控标准

  • 核心指标监控:消息发送成功率、消费成功率、消费TPS、消息堆积量、消费延迟

  • 异常告警监控:发送失败、消费异常、死信消息、节点离线、超大堆积实时告警

  • 链路追踪:全程记录消息生产、投递、消费、重试轨迹,实现问题秒级定位

十七、高频面试深度拓展问答

17.1 事务消息超时回查次数与间隔怎么配置?

RocketMQ默认事务回查最大15次,间隔逐次递增,生产环境可根据业务调整回查频次。订单业务生命周期较短,可适当缩短回查间隔,快速判定事务状态;长耗时业务可延长间隔,减少无效回查对数据库的压力。同时结合数据库持久化日志,避免回查失效。

17.2 顺序消息能否保证全局绝对有序?

无法保证全局绝对有序,仅能保证单队列局部有序。全局有序需要所有消息投递至同一队列、单线程消费,吞吐量极低,不适合高并发订单业务。电商业务仅需保证单个订单状态有序,无需全局有序,通过订单号作为分区Key即可满足业务需求。

17.3 死信消息如何实现自动恢复?

生产不建议自动恢复死信消息,脏消息自动重试会重复触发异常、占用资源。标准方案为死信消息归档存储、实时告警,人工排查问题后,通过后台补发接口手动恢复,保证每一条异常消息都可追溯、可修复。

17.4 高并发场景下如何优化消息吞吐量?

多维度组合优化:开启批量收发消息、调整客户端拉取参数、优化消费并发、部署集群多消费节点、开启读写分离、剥离消费阻塞逻辑,同时关闭无效日志、优化序列化方式,全方位提升集群吞吐能力。

十八、项目最终总结

本手册完整落地RocketMQ5在电商订单业务中的所有生产级能力,从基础消息使用、核心场景适配,到工程化规范、集群高可用、故障排查、线上调优,形成一套完整的落地体系。所有代码摒弃Demo化模拟逻辑,贴合线上真实业务场景,解决消息丢失、重复、乱序、堆积四大核心痛点,同时适配高并发、高可用、高容错的生产要求。整套方案可直接用于项目开发、生产上线,同时覆盖面试高频考点,兼具实战价值与学习价值。