定时/延时消息在RocketMQ 4.x到RocketMQ 5.0的演变:从固定延时等级到精准延时时间

原因

在消息中间件的实际应用中,延时消息是保障业务时序性的关键能力,RocketMQ 作为主流消息中间件,其 4.x 版本的延时消息机制已服务多年,但固定延时等级的设计逐渐难以满足灵活的业务需求。​

RocketMQ 5.0 针对性地重构了延时消息架构,实现了从 "固定等级" 到 "任意精度" 的跨越。本文将通过对比 4.x 与 5.0 的处理流程,拆解这一重要演进。​

RocketMQ 4.x的延时消息

Producer将消息发送到Broker后,Broker会首先将消息写入到commitlog文件,然后需要将其分发到相应的consumequeue。

不过,在分发之前,系统会先判断消息中是否带有延时等级。若没有,则直接正常分发;若有则需要经历一个复杂的过程:

修改延时消息

  • 修改消息的Topic为SCHEDULE_TOPIC_XXXX
  • 根据延时等级,在consumequeue目录中SCHEDULE_TOPIC_XXXX主题下创建出相应的queueId 目录与consumequeue文件(如果没有这些目录与文件的话)。
  • 修改消息索引单元内容。索引单元中的Message Tag HashCode部分原本存放的是消息的Tag的 Hash值。现修改为消息的投递时间。
  • 投递时间 = 消息存储时间 + 延时等级时间
  • 将消息索引写入到SCHEDULE_TOPIC_XXXX主题下相应的consumequeue中

投递时间是指该消息被重新修改为原Topic后再次被写入到 commitlog中的时间。

消息存储时间指的是消息 被发送到Broker时的时间戳

投递延时消息

  • Broker内部有⼀个延迟消息服务类,其会按照消息的投递时间消费SCHEDULE_TOPIC_XXXX中的消息

ScheuleMessageService在Broker启动时,会创建并启动一个定时器Timer,用于执行相应的定时任务。系统会根据延时等级的个数,定义相应数量的TimerTask,每个TimerTask负责一个延迟等级消息的消费与投递。

  • 从commitlog 中将原来写入的消息再次读出,并将其原来的延时等级设置为0,即原消息变为了一条不延迟的普通消息。然后再次将消息投递到目标Topic中。

局限​

  • 延时精度受限于固定等级(如默认等级仅支持 1s、5s、10s 等,无法自定义 1.5s 或 300ms);
  • 所有延时消息共享 SCHEDULE_TOPIC_XXXX,高并发下易出现队列拥堵;
  • 占用 Tag HashCode 字段,可能影响基于 Tag 的过滤逻辑。

RocketMQ 5.0 的延时消息

5.0 版本打破固定等级束缚,引入 "精确延时" 设计

优化维度​ 4.x 实现​ 5.0 实现​
延时设置方式​ 固定延时等级(delayLevel)​ 任意时间戳(deliveryTimeMs)​
临时存储载体​ 全局共享 SCHEDULE_TOPIC_XXXX​ Topic 专属延时队列​
投递时间存储​ 占用 Tag HashCode 字段​ 消息属性单独存储​
调度精度​ 秒级(依赖等级)​ 毫秒级(时间轮调度)​

流程

5.0 延时消息流程以 "自由时间戳" 为核心,无需依赖等级映射,具体如下:​

  1. 写入 CommitLog 并标记延时

Producer 通过 setDeliveryTimeMs(timestamp) 直接指定消息的绝对投递时间(如 System.currentTimeMillis () + 3500 表示延时 3.5 秒)。Broker 收到消息后:​

  • 先写入 CommitLog 完成持久化;
  • 读取消息属性中的 deliveryTimeMs,标记为 "延时消息"。
  1. 分发至 Topic 专属延时队列​

Broker 不再将所有延时消息汇总到 SCHEDULE_TOPIC_XXXX,而是为每个原 Topic 创建专属的延时队列(特殊 ConsumeQueue)。消息根据 deliveryTimeMs 按时间顺序排序存入对应延时队列,实现 "先到先投" 的基础调度逻辑。​

  1. PreciseDelayScheduler 精准调度

Broker 后台引入全新调度器 PreciseDelayScheduler,基于多层时间轮(类似时钟的时针、分针、秒针)实现高效调度:​

  • 底层时间轮处理毫秒级短期任务,高层处理分钟级、小时级长期任务;
  • 消息按投递时间划入对应 "时间槽",到期后自动降级到下层时间轮,直至触发调度;
  • 相比 4.x 单等级 TimerTask,时间轮支持百万级消息的毫秒级调度,并发能力大幅提升。
  1. 恢复原 Topic 并分发消费

当消息到达投递时间,调度器触发如下操作:​

  • 从延时队列中取出消息,恢复其原 Topic、Tag 等元数据;
  • 将消息重新写入 CommitLog(生成新的偏移量);
  • 最终分发到原 Topic 的目标 ConsumeQueue,消费者即可正常拉取消费。

总结

  • 灵活:支持任意毫秒级延时,无需修改 Broker 配置即可适配多样化业务场景(如秒杀订单 1.8 秒倒计时、定时任务精确到 100ms 触发);
  • 高性能:Topic 专属延时队列避免全局拥堵,多层时间轮调度降低并发冲突;
  • 兼容性:保留对 4.x 延时等级的支持,老系统升级无需修改代码;
  • 逻辑解耦:投递时间通过消息属性存储,不再占用 Tag 字段,不影响原有业务逻辑。

相关推荐
柳贯一(逆流河版)1 天前
RocketMQ 实战:马拉松系统异步化与延时任务落地(含死信队列处理)
rocketmq
DoveLx3 天前
RabbitMQ:构建高可用异步通信系统的基石
消息队列·rabbitmq
koping_wu3 天前
【RocketMQ】架构原理、消息丢失、重复消费、顺序消费、事务消息
架构·rocketmq·java-rocketmq
笨手笨脚の4 天前
Kafka-1 初识消息引擎系统
分布式·kafka·消息队列·消息引擎系统
Savvy..4 天前
消息队列MQ
kafka·消息队列·rabbitmq·rocketmq·mq
Jabes.yang4 天前
互联网大厂Java面试:从缓存技术到安全框架的深度探索
消息队列·java面试·缓存技术·互联网大厂·安全框架
235164 天前
【MQ】RabbitMQ:架构、工作模式、高可用与流程解析
java·分布式·架构·kafka·rabbitmq·rocketmq·java-rabbitmq
会跑的葫芦怪4 天前
RocketMQ 与 RabbitMQ 全面对比:架构、性能与适用场景解析
架构·rabbitmq·rocketmq
T.O.P_KING4 天前
事务消息(Transactional Message)
消息队列
NiKo_W4 天前
Linux 进程通信——基于责任链模式的消息队列
linux·服务器·消息队列·责任链模式·进程通信