RocketMQ系列5——5.X版本延迟消息

本文介绍了RocketMQ 5.x版本的延迟消息功能,包括延迟消息的业务场景、新增的自定义延迟配置,以及延时消息的实现原理。详细阐述了Broker内部流程,如消息如何被存入特定Topic,通过时间轮机制定时处理,最终到期后转移至实际Topic。
摘要由CSDN通过智能技术生成

        延迟消息是在业务场景中比较常用的功能,可以作为延迟队列。比如订单n分钟未支付自动取消,活动倒计时,定时发消息都可以使用到延迟消息。 

一、延迟消息介绍

        5.X版本比4.X版本新增了配置,支持自定义任意延迟时间。延迟消息实现任意时间的任务是基于时间轮做的,时间轮存储定时任务的环形队列,底层采用数组实现,数组中的每一个元素可以存放一个定时任务。在rocketMQ中,使用TimerWheel(类似定长的数组)对于时间轮进行描述和存储,TimerWheel中的每一格代表着一个时间刻度,同时会有一个firstPos指向这个时刻下所有定时消息的首条TimerLog记录的地址,一个lastPos指向这个时刻下所有定时消息的最后一条TimerLog的记录的地址,同一个刻度的消息,TimerLog使用prevPos串联成一个链表。TimerLog记录内存储元数据用于时间到了把消息转移到实际的topic里面。时间轮一格一格向前走配合TimerLog依次读出到期的消息达到定时的目的。

TimerWheel和TimerLog的关系图:

 二、生产者延迟消息举例

Message msg = new Message(topic, tag, body);  
Long deliverTimeStamp = System.currentTimeMillis() + 3000;//消息在3秒后投递
//指定延迟时间(当前时间之后)。最大40天(超过失败),单位毫秒(ms)
msg.setDeliveryTimestamp(deliverTimeStamp) 

三、延时消息实现原理

RocketMQ延迟消息在Broker内部流程图:

步骤说明:

(1)broker接受接收到消息,如果请求参数包含了延迟时间,就会把消息投递到Topic为rmq_sys_wheel_timer的队列中,保存原始的topic信息。

(2)针对放置定时消息的service,每50ms从commitLog读取指定主题(rmq_sys_wheel_timer)的定时消息。TimerEnqueueGetService从commitLog读取得到定时主题的消息,并先将其放入enqueuePutQueue。

//org.apache.rocketmq.store.timer.TimerMessageStore.TimerEnqueueGetService#run
class TimerEnqueueGetService extends ServiceThread {
    .....
    @Override
    public void run() {
        TimerMessageStore.LOGGER.info(this.getServiceName() + " service start");
        while (!this.isStopped()) {
            try {
                //读取消息放入enqueuePutQueue
                if (!TimerMessageStore.this.enqueue(0)) {
                    waitForRunning(100 * precisionMs / 1000);
                }
            } catch (Throwable e) {
                TimerMessageStore.LOGGER.error("Error occurred in " + getServiceName(), e);
            }
        }
        TimerMessageStore.LOGGER.info(this.getServiceName() + " service end");
    }
}
//org.apache.rocketmq.store.timer.TimerMessageStore#enqueue
public boolean enqueue(int queueId) {
    .....
    //从commitLog读取指定主题(rmq_sys_wheel_timer)的定时消息
    ConsumeQueue cq = (ConsumeQueue) this.messageStore.getConsumeQueue(TIMER_TOPIC, queueId);
    if (null == cq) {
        return false;
    }
    .....
    //把读取到的消息先放入enqueuePutQueue
    if (enqueuePutQueue.offer(timerRequest, 3, TimeUnit.SECONDS)) {
        break;
    }
    .....
}

(3)另一个线程TimerEnqueuePutService将enqueuePutQueue

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值