一、项目能力总览
本项目基于 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化模拟逻辑,贴合线上真实业务场景,解决消息丢失、重复、乱序、堆积四大核心痛点,同时适配高并发、高可用、高容错的生产要求。整套方案可直接用于项目开发、生产上线,同时覆盖面试高频考点,兼具实战价值与学习价值。