注意各种mq,对于延时消息的实现思路都是基本一样的:
Broker端内置延迟消息处理能力,将延迟消息通过一个临时存储进行暂存,到期后才投递到目标Topic中。如下图所示:
步骤说明如下:
- producer要将一个延迟消息发送到某个Topic中
- Broker判断这是一个延迟消息后,将其通过临时存储进行暂存。
- Broker内部通过一个延迟服务(delay service)检查消息是否到期,将到期的消息投递到目标Topic中。这个的延迟服务名字为delay service,不同消息中间件的延迟服务模块名称可能不同。
- 消费者消费目标topic中的延迟投递的消息
**显然,临时存储模块和延迟服务模块,是延迟消息实现的关键。**上图中,临时存储和延迟服务都是在Broker内部实现,对业务透明。
如下图为:RocketMQ延时消息内部流转图
可以看到RocketMQ对于延时消息的实现思路是这样的:
消息进来首先存入commitLog,识别为延时消息后,送入延时消息特有的Topic中(相当于是会对原本的消息topic做一个修改,前缀是SCHEDULE_TOPIC_XXX),之后送入延时服务,在延时服务中做延时(定时器Timer),延迟时间到后,进入commitLog(commitLog是存储消息实体的地方,而ConsumeQueue是存储消息偏移的地方),在进入目标Topic的ConsumeQueue中(这里的目标Topic指的就是原本的那个Topic,在没有修改前的),然后进入consumer消费。
先掌握大体思路,接下来我们从源码角度看下:
第一步:修改消息Topic名称和队列信息
RocketMQ Broker端在存储生产者写入的消息时,首先都会将其写入到CommitLog中。之后根据消息中的Topic信息和队列信息,将其转发到目标Topic的指定队列(ConsumeQueue)中。
由于消息一旦存储到ConsumeQueue中,消费者就能消费到,而延迟消息不能被立即消费,所以这里将Topic的名称修改为SCHEDULE_TOPIC_XXXX,并根据延迟级别确定要投递到哪个队列下。