延时消息及代码实现

延时消息:

  当消息写入broker后,在指定的时长后才可被消费处理。采用RocketMQ的延时消息可以实现定时任务的功能,而无需使用定时器。

延时消息的延迟时长不支持随意时长的延迟,是通过特定的延迟等级来指定的。

延迟等级是从1开始计数的,从1开始及其后面的每个延迟等级对应的时间是1s,5s,10s,30s,1m,2m,3m,4m,5m,6m,7m,8m,9m,10m,20m,30m,1h,2h等等。如果需要自定义延迟等级,可以通过在broker加载的配置中新增配置,messageDelayLevel=...新增时间写在后面就行。

producer将消息发送到broker后,broker会先将消息写入到commitlog文件,然后需要将去分发到相应的consumequeue。不过在分发之前,系统会先判断消息中是否带有延迟等级。若没有直接发送,有的话就会经历一个复杂的过程:

  1、修改消息的topic为SCHEDULE_TOPIC_XXXX(topic名就是.._..._XXXX,X不会被替换)

  2、根据延时等级,在consumerqueue目录中SCHEDULE_TOPIC_XXXX主题下创建出相应的queueId目录与consumerqueue文件。(延时等级delayLevel与queueId的对应关系为queueId=delayLevel-1,且创建queueId时,并不是一次性将所有延迟等级对应的目录全部创建完毕,而是用到哪个延时等级就创建哪个)。每个queue中的消息是按消息投递时间排序的,一个queue中的消息的延时消息是一样的,但topic可能不同。

  3、修改消息索引单元内容。索引中Message Tag HashCode部分原本存放的消息是Tag的Hash值。先修改为投递时间。投递时间是指该消息被重新修改为原topic后再次被写入到commitlog中的时间。投递时间=消息存储时间+延时等级时间。消息存储时间是消息发送到broker时的时间戳.

投递延时消息:broker内部有一个延时消息服务类ScheuleMessageService,其会消费SCHEDULE_TOPIC_XXXX中的消息,即按照每条消息的投递消息,将延时消息投递到目标topic中。不过在投递之前,会读出commitlog中将写入的消息,将其延时等级设置为0,即消息变成了不延时消息。然后再投递给topic。

ScheuleMessageService在broker启动时,会创建一个定时器Timer,用于执行相应的定时任务。系统会根据延时等级的个数,定义相应数量的TimerTask,每个TimerTask负责一个延时等级消息的消费与投递。每个TimerTask都会检测相应Queue队列的第一条消息是否到期。

package com.example.test.delay;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DelayProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("localhost:9876");
        producer.setSendMsgTimeout(20000);
        producer.start();

        try {
            for (int i = 0; i < 10; i++) {
                byte[] body = ("Hi" + i).getBytes();
                Message msg = new Message("TopicTest", "tag", body);
                //指定消息延时等级
                msg.setDelayTimeLevel(3);
                SendResult sendResult = producer.send(msg);
                //输出消息被发送时间
                System.out.println(new SimpleDateFormat("mm:ss").format(new Date()));
                System.out.println(sendResult);
            }
        }finally {
            producer.shutdown();
        }
    }
}

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值