【RabbitMQ】之延时队列

RabbitMQ 延时队列(实现定时任务)


1、定时任务的场景

购物下单等待支付的订单,超过一定时间后,系统就会自动取消订单并释放占有的资源。

常用的解决方案是:使用 Spring 的 Schedule 定时任务轮询数据库。但缺点很大:消耗系统内存、增加了数据库的压力、存在较大的时间误差。

较好的解决方案是使用 RabbitMQ 的延时队列:RabbitMQ 消息的 TTL 和死信 Exchange 结合。


2、消息的 TTL(Time To Live)

消息的 TTL 就是消息的存活时间。RabbitMQ 可以对队列和消息分别设置 TTL:

  • 对队列设置 TTL 就是队列没有消费者连着的保留时间,也可以对每个消息单独设置。超过了这个时间,我们就认为这个消息死了,这种消息称之为死信,而保存死信的队列就被称之为死信队列
  • 如果同时对队列和消息设置,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息的死亡时间有可能不一样。

在这里,只有设置单个消息的 TTL 才是实现延时任务的关键。可以通过设置消息的 expiration 字段或者 x-message-ttl 属性来设置时间,两者效果一样的。


3、死信路由交换机 - Dead Letter Exchange(DLX)

一个消息在满足如下条件时会被放入到死信路由中(什么是死信):

  • 一个消息被 Consumer 拒收了,并且 reject 方法的参数里 requque 是 false,也就是说不会被再次放入到队列中被其它消费者消费;
  • 消息的 TTL 到了,消息过期了;
  • 队列的长度限制满了,排在前面的消息会被丢弃或者扔到私信路由上。

Dead Letter Exchange 其实就是一种普通的 exchange,和其他的 exchange 没有两样,只是在某一个设置 Dead Letter Exchange 的队列中有消息过期了,会自动触发消息的转发,发送到 Dead Letter Exchange 中去。

我们既可以控制消息在一段时间后变成私信,又可以控制死信的信息被路由到某一个指定的交换机中,结合二者就可以实现一个延时队列。


4、延时队列原理

在这里插入图片描述

其工作原理和流程如下:

  1. 延时队列 order-delay-queue 和消费者监听的队列 order-release-queue 同时和 order-event-exchange 交换机绑定,延时队列和交换机的路由键为 order-create,消费者队列的路由键为 order-release-queue
  2. 生产者发送消息到 exchange 中,路由键设置为 order-create,然后交换机把消息投递到延时队列中;
  3. 由于消息设置了超时时间 x-message-ttl 为 60 秒,死信队列路由交换机为 order-event-exchange,死信路由键为 order-release,所以当存放的消息到达指定的 60 秒后,就会变成私信并投递到指定的死信路由交换机中,这样,消息又重新回到了的了 order-event-exchange 交换机中;
  4. 由于死信路由键已经重新设置为了 order-release,所以,交换机再次根据该路由把消息投递到 order-release-queue 队列中,最后交给消费者处理。

在整个流程中,我们服用了 order-event-exchange 交换机,而延时队列只作为存放延时消息的队列不被任何消费者监听。


5、延时队列应用案例

根据上面的原理和流程,下面我们来编写一个延时队列的案例:

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

@Configuration
public class MyRabbitMQConfig {

    // 把 Queue、Exchange、Binding 对象的创建交给 Spring 管理

    // 创建交换机
    @Bean
    public Exchange orderEventExchange() {
        /* 交换机类型为 TopicExchange
         *   String name,
         *   boolean durable,
         *   boolean autoDelete,
         *   Map<String, Object> arguments
         */
        return new TopicExchange("order-event-exchange", true, false);

    }

    // 创建死信队列
    @Bean
    public Queue orderDelayQueue() {
        /*
         *  Queue(String name,  队列名字
         *  boolean durable,  是否持久化
         *  boolean exclusive,  是否排他
         *  boolean autoDelete, 是否自动删除
         *  Map<String, Object> arguments) 属性
         */
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange");
        arguments.put("x-dead-letter-routing-key", "order.release");
        arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
        Queue queue = new Queue("order.delay.queue", true, false, false, arguments);

        return queue;
    }

    // 消费者队列
    @Bean
    public Queue orderReleaseQueue() {

        Queue queue = new Queue("order.release.queue", true, false, false);

        return queue;
    }

    // 绑定延时队列和交换机
    @Bean
    public Binding orderCreateBinding() {
        /*
         * String destination, 目的地(队列名或者交换机名字)
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * String exchange,
         * String routingKey,
         * Map<String, Object> arguments
         * */
        return new Binding("order.delay.queue", Binding.DestinationType.QUEUE,
                           "order-event-exchange", "order.create", null);
    }

    // 绑定消费者队列和交换机
    @Bean
    public Binding orderReleaseBinding() {

        return new Binding("order.release.queue", Binding.DestinationType.QUEUE,
                           "order-event-exchange", "order.release.order", null);
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值