这篇笔记主要记录延迟队列的实现原理
总结来说:就是producer发送了一个请求之后,broker在收到请求时,会先判断当前请求中(消息中)的延迟级别是否不为null,不为null,就表示当前消息是延迟消息,就会根据程序员设置的延迟级别,将消息投递到指定的topic中,并且设置到期时间,在到期了之后,将消息从指定的topic中取出来,投递到真正程序员指定的topic和queue中
putMessage
putMessage方法是broker作为netty服务端,接收到producer的send请求之后,去处理消息的方法,在调用这个方法之前,还有多层的逻辑判断,暂时先不深究
org.apache.rocketmq.store.CommitLog#putMessage
if (msg.getDelayTimeLevel() > 0) {
// 如果延迟级别超过最大级别,就设置延迟级别为18
if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {
msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel());
}
// 设置延迟消息的topic和queue
topic = ScheduleMessageService.SCHEDULE_TOPIC;
queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel());
// Backup real topic, queueId
// 将真正的topic和queueId存起来,存到property属性中
MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic());
MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId()));
msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
// 将消息的topic和queue替换为延迟队列的
msg.setTopic(topic);
msg.setQueueId(queueId);
}
ScheduleMessageService#start
这个类是处理延迟队里的关键类,是在broker启动的时候,会取启动这个异步线程,我们先看下异步线程中做的事情
/**
* 在启动broker的时候,会初始化这里的timer
* 并且会根据延迟级别,创建对应的timer任务
*/
public void start() {
if (started.compareAndSet(false, true)) {
this.timer = new Timer("ScheduleMessageTimerThread", true);
// 这里for循环,会为每一个延迟级别创建一个延迟任务
for (Map.Entry<Integer, Long> entry : this.delayLevelTable.entrySet()) {
/**
* level是设置的延迟级别
*/
Integer level = entry.getKey();
/**
* value是延迟级别所对应的延迟时间
* 以及对应的offset 偏移量?
*/
Long timeDelay = entry.getValue();
Long offset = this.offsetTable.get(level);
if (null == offset) {
offset = 0L;
}
// 初始化时,第一次延迟时间是1S,在后面任务执行之后(DeliverDelayedMessageTimerTask),会修改任务延迟时间
if (timeDelay != null) {
this.timer.schedule(new DeliverDelayedMessageTimerTask(level, offset), FIRST_DELAY_TIME);