大白话设计RocketMQ延迟消息

延迟消息一般用于:提前发送消息,延迟一段时间后才需要被处理的场景 。比如:下单半小时后还未支付,则取消订单 释放库存 等 。
RocketMQ的延迟消息使用上非常便捷 , 但是不支持任意时间的延迟,这一点对于有强迫症的朋友来说就比较难受,但是搞明白为什么这么设计后 , 就自然释怀了 。
为什么RocketMQ不支持任意时间的延时?为什么延迟时间只能是从1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h这些时间段里?。咳绻?媚憷瓷杓芌ocketMQ的延迟消息 , 你会怎么设计?本文从以上几个问题聊聊RocketMQ的延迟消息 。

大白话设计RocketMQ延迟消息

文章插图
一、使用延迟消息RocketMQ不支持任意时间的延迟,只有18个等级的延迟时间,默认是:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 。从头到尾共18个等级时间,s、m、h、d , 分别表示秒、分、时、天 。
默认的18个等级对应的时间可以修改,在broker.conf中增加如下配置,根据自身需求修改时间,然后重启broker 。
【大白话设计RocketMQ延迟消息】messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2hRocketMQ发送延迟消息只需要给消息设置延迟时间级别setDelayTimeLevel 。
DefaultMQProducer producer = new DefaultMQProducer("TestProducerGroup");Message rocketMsg = new Message(topic, tags, payloads);// delayLevel=0时,无需延迟if (delayLevel > 0) {rocketMsg.setDelayTimeLevel(delayLevel);}SendResult sendResult = producer.send(rocketMsg, timeout);二、延迟消息的原理1.你会怎么设计如果让你来设计RocketMQ的延迟消息,你会怎么设计?笔者会这样设计:
  • 延迟消息也是个消息 , 只是多了延迟时间,既然是消息,不管是不是要立刻处理,先找个临时Topic存储起来 。
  • Topic里面实际上是一个个队列,那所有的延迟消息要存在一个队列里吗?不要放在同一个队列里,因为消息各自都有不同的延迟时间,如果放在一个队列里,会牵扯到其余问题:比如排序、比如记录消费位置等 。所以是按延迟时间分开存 。
  • 消息已经存起来了,那怎么处理呢?既然涉及到了延迟时间,那自然启动线程去定时获取消息,判断消息的延迟时间是否已经到达,到达之后则取出来投放到目的Topic 。
2.粗略架构图讲到这里,延迟消息的架构图基本浮现出来了:
大白话设计RocketMQ延迟消息

文章插图
3.RocketMQ的设计实际上RocketMQ在设计延迟消息时,跟上面的思路基本类似,不在赘述 , 额外补充几点:
  • 消息进入Broker后,会被存储在TopicSCHEDULE_TOPIC_XXXX中,只是在Dashboard中看不到 。
  • TopicSCHEDULE_TOPIC_XXXX中有18个消息队列,分别存储18个延迟等级对应的消息 。
  • RocketMQ 在启动时,会从broker.conf中获取18个等级对应的时间,延迟等级和时间的关系会存在放到DelayLevelTable中 。
  • RocketMQ会开启18个定时任务每隔100ms,从TopicSCHEDULE_TOPIC_XXXX判断18个队列里的第一个消息是否可以被投放,如果可以投放 , 则在投放到原本的目的Topic 。判断逻辑:存入时间+延迟时间 > 当前时间 。
三、为什么不支持自定义延时时间说到这里,估计你也能猜到,为什么不支持自定义延迟时间了 , 核心原因还是性能问题 。
试想一下,如果设计成任意时间,那么就不可能使用18个队列了,更不可能使用无限个队列了,只可能使用单个队列 。
但是如果使用单个队列,按照先进先出的存放的话,那出现需要后进先出的消息怎么办?那只能对整个队列进行排序 , 如果消息量很大,每次有消息进来都需要排序,那CPU肯定会被玩爆 。
而且队列里的消息被消费后,都会记录偏移量,如果每次有消息进来都要排序,那偏移量则失去意义,增加了消息丢失的风险 。
所以,RocketMQ的这种18个延迟时间等级的设计 , 虽然在延迟时间的自由度上作出了妥协,但是基本满足业务,性能也很优秀 。
四、总结本文聊了RocketMQ延迟消息的使用、原理、解答了部分疑问 。核心概念:临时Topic、目的Topic、定时任务、18个延迟等级、18个消息队列 。RocketMQ延迟消息的设计方式 , 是一种兼顾了性能和业务的优秀设计 。


推荐阅读