RocketMQ延迟消息是怎么实现的?如果让你设计一个延迟消息系统你会怎么做?
🚀 RocketMQ延迟消息是怎么实现的?如果让你设计一个延迟消息系统你会怎么做?
RocketMQ的延迟消息本质上并不是把消息直接存储到未来时间队列,而是采用“时间轮思想 + 多级延迟队列 + 定时任务扫描”的设计。发送延迟消息时先进入特殊Topic,Broker后台线程定时扫描到期消息并重新投递到真实Topic。其核心目标是避免海量定时器导致内存爆炸,同时保证高吞吐、高可靠和可扩展能力。
1️⃣ 问题背景
延迟消息是消息队列系统中非常常见的能力。
例如:
- 订单30分钟未支付自动取消
- 外卖超时自动退款
- 优惠券7天后过期
- 直播预约提醒
- 短信定时发送
- 会员到期通知
- 物流超时提醒
假设电商平台每天产生1000万订单。
每个订单都需要:
如果直接创建1000万个Java定时任务:
因此消息中间件必须提供高性能延迟消息能力。
2️⃣ 核心原理
很多开发者认为延迟消息实现方式是:
↓
启动定时器
↓
到时间触发
↓
投递消费者
实际上RocketMQ并不是这样实现。
因为:
- 消息量可能达到亿级
- 定时器数量无法控制
- Broker重启后定时器丢失
- 维护成本极高
RocketMQ采用:
↓
进入延迟Topic
↓
存储CommitLog
↓
定时扫描线程
↓
到期重新投递
↓
进入真实Topic
↓
消费者消费
3️⃣ 数据结构分析
真实Topic
正常订单消息存放位置。
延迟Topic
RocketMQ内部保留Topic。
所有延迟消息先写入这里。
延迟级别映射
5s
10s
30s
1m
2m
5m
10m
30m
1h
2h
每个延迟级别对应一个Queue。
例如:
DelayLevel 2 → Queue2
DelayLevel 3 → Queue3
这样避免所有延迟消息堆积在一个队列中。
消息存储结构
真实QueueId
投递时间
消息体
延迟消息只是临时修改Topic和Queue信息。
4️⃣ 算法分析
发送阶段
发送:
Broker收到消息:
↓
替换Topic
↓
写入SCHEDULE_TOPIC_XXXX
扫描阶段
Broker启动后台线程:
每秒扫描一次。
核心逻辑:
↓
判断是否到期
↓
未到期继续等待
↓
已到期重新投递
时间复杂度:
但由于按延迟等级分桶,因此扫描成本可控。
5️⃣ 执行流程
RocketMQ延迟消息完整流程
↓
指定延迟等级
↓
Broker修改Topic
↓
进入SCHEDULE_TOPIC_XXXX
↓
写入CommitLog
↓
后台线程扫描
↓
判断是否到期
↓
恢复真实Topic
↓
重新投递
↓
消费者消费
为什么性能高?
- 没有海量Timer
- 没有大量线程
- 利用顺序写CommitLog
- 利用时间分桶降低扫描范围
6️⃣ 实际案例
订单超时取消
用户下单:
↓
发送30分钟延迟消息
消息内容:
30分钟后:
↓
重新进入OrderTimeoutTopic
↓
消费者检查订单状态
↓
未支付则取消订单
延迟消息数量估算
假设:
- 每天订单1000万
- 延迟30分钟
- 平均消息1KB
存储:
RocketMQ完全可以支撑。
7️⃣ 如果让我设计延迟消息系统
第一阶段:简单版
↓
execute_time索引
↓
定时任务扫描
适合百万级数据。
第二阶段:Redis ZSet方案
member = msgId
定时扫描:
↓
获取到期消息
↓
发送MQ
适合千万级场景。
第三阶段:时间轮方案
↓
秒槽位
↓
分钟槽位
↓
小时槽位
↓
触发投递
类似Kafka Timer Wheel。
时间复杂度:
适合亿级消息系统。
第四阶段:最终架构
↓
Delay Topic
↓
Time Wheel
↓
Schedule Service
↓
Real Topic
↓
Consumer
同时增加:
- 多副本存储
- 失败重试
- 死信队列
- 消息追踪
- 幂等控制
8️⃣ 面试常见问题
RocketMQ为什么不用定时器?
因为海量定时器会导致内存和线程资源消耗巨大,不适合分布式消息系统。
延迟消息为什么要二次投递?
这样可以复用现有消息存储和消费体系,避免设计全新的调度架构。
RocketMQ延迟消息有什么缺陷?
- 早期版本仅支持固定延迟等级
- 不是绝对精确时间
- 依赖Broker扫描线程
Redis ZSet和时间轮哪个好?
千万级以内ZSet简单高效;亿级以上时间轮更节省资源。
如果你设计延迟消息系统怎么做?
9️⃣ 总结
✅ RocketMQ延迟消息本质是延迟存储加二次投递。
✅ 消息首先进入SCHEDULE_TOPIC_XXXX。
✅ Broker后台线程定期扫描到期消息。
✅ 到期后恢复真实Topic并重新发送。
✅ RocketMQ避免了海量Timer带来的资源消耗问题。
✅ 如果自研延迟消息系统,推荐演进路线:
↓
Redis ZSet
↓
多级时间轮
↓
MQ二次投递
↓
分布式延迟消息平台
这也是当前大型互联网公司在订单超时、支付超时、定时通知、营销触达等场景下最主流的延迟消息设计方案。
上一篇:分布式ID生成器有哪些?
相关文章
-
接口优化的几种方法
使用那种方法进行接口优化,取决于不同的业务场景,常见的优化方法:
NEW个对象 2024-10-22
-
Nacos与Eureka如何选择?
1、Nacos支持AP和CP两种模式,Eureka仅支持AP,如果对数据一致性要求比较高,选择nacos。 2、Nacos支持多语言,比如:java、python、go等,Eureka仅支持java 3、Nacos是Spring alibaba的组件,Eureka是Netflix的组件。 4、Nacos 不仅提供服务注册与发现,还提供配置管理、动态 DNS 服务等。
NEW个对象 2024-12-30
-
认证授权:OAuth2简介及四种授权模型详解
认证授权:OAuth2简介及四种授权模型详解
NEW个对象 2026-06-11