五.RocketMQ极简入门-RocketMQ延迟消息

使用场景

我们通常使用定时任务比如Quartz来解决超时业务,比如:订单支付超时关单,VIP会员超时提醒。但是使用定时任务来处理这些业务场景在数据量大的时候并不是一个很好的选择,会造成大量的空扫描浪费性能。我们可以考虑使用延迟消息来解决。

概述

延迟消息即:把消息写到Broker后需要延迟一定时间才能被消费 , 在RocketMQ中消息的延迟时间不能任意指定,而是由特定的等级(1 到 18)来指定,分别有:

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

可以通过修改配置来增加级别,比如在mq安装目录的 broker.conf 文件中增加如:

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 2d	

增加了2d 两天,这个时候总共就有19个level。

延迟消息工作原理

延迟队列工作流程图
在这里插入图片描述
RocketMQ Broker端在存储生产者写入的消息时,首先都会将其写入到CommitLog中。之后根据消息中的Topic信息和队列信息,将其转发到目标Topic的指定队列(ConsumeQueue)中。不过,在分发之前,系统会先判断消息中是否带有延时等级。若没有,则直接正常分发;如果有就走延迟队列,执行下面的流程

  1. 修改消息Topic的名字为SCHEDULE_TOPIC_XXXX

  2. 根据延时等级,在consumequeue目录中SCHEDULE_TOPIC_XXXX主题下创建出相应的queueId
    目录与consumequeue文件

  3. 修改消息索引单元,计算出的投递时间当做消息Tag的哈希值存储到CosumeQueue中,投递时间 = 消息存储时间 + 延时等级时间 。下面是CosumeQueue单个存储单元组成结构如下
    在这里插入图片描述

  • Commit Log Offset:记录在CommitLog中的位置。
  • Size:记录消息的大小
  • Message Tag HashCode:记录消息Tag的哈希值,用于消息过滤。特别的,对于延迟消息,这个字段记录的是消息的投递时间戳。
  1. 将消息索引写入到SCHEDULE_TOPIC_XXXX主题下相应的consumequeue中

  2. Broker内部有⼀个延迟消息服务类ScheuleMessageService,根据延迟级别数,创建对应数量的定时器Timer,定时消费SCHEDULE_TOPIC_XXXX中的消息,并投递到目标Topic中。

  3. 在将消息到期后,队列的Level等级改为0,作为一条普通消息,投递到目标Topic。

延迟消息实战

消息发送方

给消息设置延迟级别,API:message.setDelayTimeLevel(3);

public class Producer {

    //演示消息同步发送
    public static void main(String[] args) throws Exception {
        //生产者
        DefaultMQProducer producer = new DefaultMQProducer("syn-producerGroup-delay");

        //设置name server地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动
        producer.start();

        for (long i = 0 ; i < 4 ; i++){


            Order order = new Order(i,"订单"+i,"创建");

            //添加内容
            byte[] bytes = (JSON.toJSONString(order)).getBytes(CharsetUtil.UTF_8);
			//创建消息,指定:TOPIC 和 TAG
            Message message = new Message("topic-order-delay","product-order-delay",bytes);
            
			//延迟级别 3,代表 10s延迟
            message.setDelayTimeLevel(3);
            
            message.setKeys("key-"+i);
            
            //执行发送
            SendResult result = producer.send(message);
            
            System.out.println("发送时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

            System.out.println(result);
        }

        producer.shutdown();
    }
}

消息消费方

public class Consumer {
    public static void main(String[] args) throws Exception {
        //创建消费者
        DefaultMQPushConsumer defaultMQPushConsumer = 
        							new DefaultMQPushConsumer("syn-consumerGroup-delay");
        //设置name server 地址
        defaultMQPushConsumer.setNamesrvAddr("127.0.0.1:9876");
        
        //从开始位置消费
        defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        //订阅
        defaultMQPushConsumer.subscribe("topic-order-delay","product-order-delay");

        defaultMQPushConsumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

                list.forEach(message->{
                    System.out.println("消费时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    System.out.println(message+" ; "+new String(message.getBody(), CharsetUtil.UTF_8));
                });

                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        defaultMQPushConsumer.start();
    }
}

从消费者消费结果时间来看,消息是延迟了10s后才收到。

文章结束,创作不易,大佬给个好评。

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨家巨子@俏如来

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值