【mq读书笔记】定时消息

mq不支持任意的时间京都,如果要支持,不可避免的需要在Broker层做消息排序,加上持久化方面的考量,将不可避免地带来巨大的性能消耗,所以rocketMQ只支持特定级别的延迟消息。

在Broker短通过messageDelayLevel配置。实现类:org.apache.rocketmq.store.schedule.ScheduleMessageService

public class ScheduleMessageService extends ConfigManager {
    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);

    public static final String SCHEDULE_TOPIC = "SCHEDULE_TOPIC_XXXX";//定时消息统一主题
    private static final long FIRST_DELAY_TIME = 1000L;//第一次调度时延迟的时间,默认1s
    private static final long DELAY_FOR_A_WHILE = 100L;//每一延时级别调度一次后延迟该时间间隔后再放入调度池。
    private static final long DELAY_FOR_A_PERIOD = 10000L;//发送一场后延迟该时间后再继续参与调度

    private final ConcurrentMap/* level */, Long/* delay timeMillis */> delayLevelTable =
        new ConcurrentHashMap(32);//延迟级别与时间

    private final ConcurrentMap/* level */, Long/* offset */> offsetTable =
        new ConcurrentHashMap(32);//延迟级别消息消费进度
    private final DefaultMessageStore defaultMessageStore;//默认消息存储器
    private final AtomicBoolean started = new AtomicBoolean(false);
    private Timer timer;
    private MessageStore writeMessageStore;
    private int maxDelayLevel;//MessageStoreConfig#messageDelayLevel中最大消息延迟级别

org.apache.rocketmq.store.DefaultMessageStore#load:

 

【mq读书笔记】定时消息_第1张图片

 

 

【mq读书笔记】定时消息_第2张图片

 

延迟消息消费队列消息进度加载+delayLevelTable数据构造。延迟队列消息消费进度默认存储路径为${ROCKET_HOME}/store/config/delayoffset

ScheduleMessageService#start:

【mq读书笔记】定时消息_第3张图片

 每一个delaylevel-1对应一个消息队列,对应一个调度任务:

org.apache.rocketmq.store.schedule.ScheduleMessageService.DeliverDelayedMessageTimerTask:

  class DeliverDelayedMessageTimerTask extends TimerTask {
        private final int delayLevel;
        private final long offset;

        public DeliverDelayedMessageTimerTask(int delayLevel, long offset) {
            this.delayLevel = delayLevel;
            this.offset = offset;
        }

        @Override
        public void run() {
            try {
                if (isStarted()) {
                    this.executeOnTimeup();
                }
            } catch (Exception e) {
                // XXX: warn and notify me
                log.error("ScheduleMessageService, executeOnTimeup exception", e);
                ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(
                    this.delayLevel, this.offset), DELAY_FOR_A_PERIOD);
            }
        }

 

 

【mq读书笔记】定时消息_第4张图片

 

根据队列ID与延迟主题 查找消息消费队列,如果未找到,说明目前并不存在该延时级别的消息,每100毫秒检查一次:

 

 

----------------

 

 

根据offset从消息消费队列中获取当前队列中所有有效消息。如果没找到,检查offset合法性之后100ms后重试:

【mq读书笔记】定时消息_第5张图片

 

----------------------------

【mq读书笔记】定时消息_第6张图片

 

遍历ConsumeQueue,解析出消息的物理偏移量,消息长度,消息tag hashcode

---------------

【mq读书笔记】定时消息_第7张图片

 

检查是否到执行时间,未到的话,延迟到正确时间执行:

【mq读书笔记】定时消息_第8张图片

 

到的话:

 

 

【mq读书笔记】定时消息_第9张图片

 

根据消息物理偏移量与消息大小从commitlog文件中查找消息。如果未找到则跳过这条消息

【mq读书笔记】定时消息_第10张图片

 

根据消息重新构建新的消息对象,清楚消息的延迟级别属性,并恢复消息原先的消息主题与消息消费队列,消息的消费次数reconsumeTime并不会丢失。将消息再次存入到commitlog。并转发到对应的消息队列上,供消费者再次消费。

【mq读书笔记】定时消息_第11张图片

 

遍历结束后,100ms后开始下一次并更新消费进度。

 

你可能感兴趣的:(【mq读书笔记】定时消息)