RocketMQ系列文章(入门篇第6篇):延时消息+顺序消息实战

前言:特殊消息场景的落地利器

普通消息只能实现基础的异步通信,而在实际业务中,我们经常会遇到定时处理、严格顺序执行的需求。比如订单超时未支付自动关闭、验证码过期失效、物流状态按序更新等场景,普通消息无法满足,这时候就需要用到RocketMQ的延时消息和顺序消息。

本篇基于SpringBoot环境,手把手实现延时消息、顺序消息的开发,讲解底层核心逻辑,落地真实业务场景,同时梳理常见问题和解决方案,让大家快速掌握这两种高频消息类型的用法。

前置准备:SpringBoot+RocketMQ整合环境正常(沿用第5篇项目),本地RocketMQ服务(4.8.0版本)正常运行,Broker开启自动创建Topic权限。

一、延时消息实战

1.1 延时消息核心认知

1. 定义

消息发送后,不会立即被消费者消费,等待指定的延时时间后,才会进入消费队列供消费者拉取,属于定时触发的消息类型。

2. 4.x版本特性

  • 不支持自定义延时时间,内置18个延时等级,通过等级映射固定延时时间

  • 核心等级速查:1s、5s、10s、30s、1m、2m、3m、4m、5m、6m、7m、8m、9m、10m、20m、30m、1h、2h

  • 等级对应数字:1=1s、2=5s、3=10s...以此类推,18=2h

3. 适用场景

  • 订单超时未支付,自动取消订单、回退库存

  • 短信/验证码过期失效

  • 会员到期提醒、待办任务定时推送

1.2 延时消息开发(SpringBoot版)

1. 生产者封装

在第5篇的RocketMQUtil中,新增延时消息发送方法,通过messageDelayLevel参数指定延时等级:

bash 复制代码
/**
 * 发送延时消息
 * @param topic 主题
 * @param tag 标签
 * @param msg 消息内容
 * @param delayLevel 延时等级(1-18)
 */
public <T> void sendDelayMsg(String topic, String tag, T msg, int delayLevel) {
    String destination = topic + ":" + tag;
    try {
        // 构建消息对象,设置延时等级
        Message<T> message = MessageBuilder.withPayload(msg).build();
        rocketMQTemplate.syncSend(destination, message, 3000, delayLevel);
        log.info("延时消息发送成功,destination:{},延时等级:{},消息内容:{}", destination, delayLevel, msg);
    } catch (Exception e) {
        log.error("延时消息发送失败,destination:{}", destination, e);
    }
}

2. 测试接口编写

在MsgController中新增延时消息测试接口,模拟订单超时关闭场景(延时30s,对应等级4):

bash 复制代码
private static final String DELAY_TOPIC = "delay_topic";
private static final String DELAY_TAG = "delay_tag";

/**
 * 测试延时消息(30s后消费)
 */
@GetMapping("/delay")
public String testDelaySend() {
    // 模拟订单号
    String msg = "订单ID:" + System.currentTimeMillis() + ",超时未支付,30s后自动取消";
    // 延时等级4=30s
    rocketMQUtil.sendDelayMsg(DELAY_TOPIC, DELAY_TAG, msg, 4);
    return "延时消息发送成功,等待30s后消费";
}

3. 延时消息消费者

创建延时消息消费者,监听对应Topic,接收延时消息并执行业务逻辑:

bash 复制代码
/**
 * 延时消息消费者
 */
@Slf4j
@Component
@RocketMQMessageListener(
        consumerGroup = "delay_consumer_group",
        topic = "delay_topic",
        selectorExpression = "delay_tag"
)
public class DelayMsgConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        try {
            log.info("延时消息消费成功,收到消息:{}", message);
            // 执行业务:取消订单、回退库存
            log.info("执行订单超时取消逻辑完成");
        } catch (Exception e) {
            log.error("延时消息消费失败", e);
            throw new RuntimeException("消费失败,触发重试");
        }
    }
}

二、顺序消息实战

2.1 顺序消息核心认知

1. 定义

严格按照消息发送的顺序进行消费,保证先发送的消息先被消费,后发送的消息后被消费。

2. 两种顺序模式

  • 分区顺序(推荐):同一业务ID的消息发送到同一个Queue,单Queue内消息有序,兼顾吞吐量和顺序性

  • 全局顺序(极少用):Topic只有一个Queue,所有消息全局有序,吞吐量极低,不适合高并发场景

3. 适用场景

  • 订单流程:创建订单→支付成功→发货→签收

  • 物流状态更新:揽收→中转→派送→签收

  • 数据同步:增量数据按顺序更新

2.2 顺序消息开发(SpringBoot版)

1. 核心原理

生产者通过业务ID哈希取模,将同一业务的消息路由到同一个Queue;消费者采用单线程消费对应Queue,保证顺序执行。

2. 顺序消息生产者

新增顺序消息发送方法,指定业务ID(如订单ID),实现分区路由:

bash 复制代码
/**
 * 发送顺序消息
 * @param topic 主题
 * @param tag 标签
 * @param msg 消息内容
 * @param bizId 业务ID(用于路由到同一Queue,保证顺序)
 */
public <T> void sendOrderlyMsg(String topic, String tag, T msg, String bizId) {
    String destination = topic + ":" + tag;
    try {
        // 同步顺序发送,基于业务ID哈希路由
        rocketMQTemplate.syncSendOrderly(destination, msg, bizId);
        log.info("顺序消息发送成功,destination:{},业务ID:{},消息内容:{}", destination, bizId, msg);
    } catch (Exception e) {
        log.error("顺序消息发送失败,destination:{}", destination, e);
    }
}

3. 测试接口(模拟订单流程)

按顺序发送订单状态消息,同一订单ID保证路由到同一Queue:

bash 复制代码
private static final String ORDER_TOPIC = "order_topic";
private static final String ORDER_TAG = "order_tag";

/**
 * 测试顺序消息(模拟订单流程)
 */
@GetMapping("/orderly")
public String testOrderlySend() {
    // 同一订单ID,保证顺序
    String orderId = "ORDER_" + System.currentTimeMillis();
    // 按顺序发送:创建→支付→发货→签收
    rocketMQUtil.sendOrderlyMsg(ORDER_TOPIC, ORDER_TAG, "订单创建:" + orderId, orderId);
    rocketMQUtil.sendOrderlyMsg(ORDER_TOPIC, ORDER_TAG, "订单支付:" + orderId, orderId);
    rocketMQUtil.sendOrderlyMsg(ORDER_TOPIC, ORDER_TAG, "订单发货:" + orderId, orderId);
    rocketMQUtil.sendOrderlyMsg(ORDER_TOPIC, ORDER_TAG, "订单签收:" + orderId, orderId);
    return "顺序消息发送成功,按流程消费";
}

4. 顺序消息消费者

配置consumeMode = ConsumeMode.ORDERLY,开启单线程顺序消费:

bash 复制代码
/**
 * 顺序消息消费者
 * consumeMode = ConsumeMode.ORDERLY:开启顺序消费
 */
@Slf4j
@Component
@RocketMQMessageListener(
        consumerGroup = "orderly_consumer_group",
        topic = "order_topic",
        selectorExpression = "order_tag",
        consumeMode = ConsumeMode.ORDERLY
)
public class OrderlyMsgConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        try {
            log.info("顺序消息消费成功,内容:{}", message);
            // 按顺序执行业务逻辑,不可乱序
            Thread.sleep(100);
        } catch (Exception e) {
            log.error("顺序消息消费失败,消息:{}", message, e);
            // 顺序消费失败,不可跳过,必须重试
            throw new RuntimeException("顺序消费失败,重试");
        }
    }
}

三、测试验证与结果查看

3.1 延时消息测试

  1. 启动SpringBoot项目,调用/mq/delay接口

  2. 观察控制台,30s后消费者才会打印消费日志

  3. 登录RocketMQ控制台,查看延时消息的生产时间、消费时间差

3.2 顺序消息测试

  1. 调用/mq/orderly接口

  2. 控制台按创建→支付→发货→签收的固定顺序打印日志

  3. 多次测试,同一订单ID的消息始终保持发送顺序,不会乱序

四、常见问题与避坑指南

延时消息避坑

  • 4.x版本仅支持固定延时等级,无法自定义秒数,5.x版本支持自定义延时时间

  • 延时消息不支持修改/撤回,发送前需确认延时等级

  • 延时消息消费失败,会进入延时重试队列,重试间隔遵循延时等级

顺序消息避坑

  • 顺序消费必须开启ConsumeMode.ORDERLY,否则为并发消费,会乱序

  • 顺序消费失败不能跳过,必须抛出异常重试,否则会导致后续消息阻塞

  • 禁止在顺序消费逻辑中做异步处理,否则会打破顺序

  • 同一消费者组内,所有消费者必须配置为顺序消费,配置需一致

五、本篇核心总结

延时消息依靠延时等级实现定时触发,适合超时处理、定时任务场景,4.x版本仅支持固定等级

  • 顺序消息通过业务ID路由+单线程消费保证有序,优先选用分区顺序,兼顾性能

  • 两种消息均需保证消费可靠性,失败抛出异常触发重试,避免数据丢失

  • SpringBoot环境下通过封装方法,可快速落地业务场景,代码可直接复用


下篇预告

入门篇第7篇:《RocketMQ消息消费可靠性:重试机制+死信队列》,详解消费失败后的重试策略、死信队列原理,实现异常消息的闭环处理,杜绝消息丢失和堆积。

相关推荐
zb200641202 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
indexsunny2 小时前
互联网大厂Java面试实战:基于微服务与云原生的电商场景问答解析
java·数据库·spring boot·docker·微服务·云原生·kubernetes
下地种菜小叶2 小时前
接口幂等怎么设计?一次讲清重复提交、支付回调、幂等键与防重落地方案
java·spring boot·spring·kafka·maven
YDS8292 小时前
大营销平台 —— 模板方法串联前中置抽奖规则
java·spring boot·ddd
喜欢流萤吖~2 小时前
SpringBoot 性能优化实战
spring boot·后端·性能优化
我登哥MVP2 小时前
【SpringMVC笔记】 - 6 - RESTFul编程风格
java·spring boot·spring·servlet·tomcat·maven·restful
yhole2 小时前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
常利兵3 小时前
Spring Boot 搭建邮件发送系统:开启你的邮件自动化之旅
spring boot·后端·自动化
彭于晏Yan3 小时前
Spring Boot 集成邮件服务实现发送邮件功能
java·spring boot·后端