rabbitmq专栏 代码地址:码云

rabbitmq实现延迟消息

  • 1. 什么是延迟队列?
  • 2. 延迟队列使用场景
  • 3. 延迟队列的实现方式
  • 3.1 利用TTL+死信队列
  • 3.2 利用RabbitMQ插件实现
  • 3.2.1 容器挂载
  • 3.2.2 上传插件
  • 3.2.3 复制到RabbitMQ的插件目录
  • 3.2.4 使插件生效
  • 4. 代码实现
  • 4.1 RabbitMQConfig配置
  • 4.2 producer
  • 4.3 consumer
  • 4.4 启动服务,测试

1. 什么是延迟队列?

延时队列,首先,它是一种队列,队列意味着内部的元素是有序的,元素出队和入队是有方向性的,元素从一端进入,从另一端取出。

其次,延时队列,最重要的特性就体现在它的延时属性上,跟普通的队列不一样的是,普通队列中的元素总是等着希望被早点取出处理,而延时队列中的元素则是希望被在指定时间得到取出和处理,所以延时队列中的元素是都是带时间属性的,通常来说是需要被处理的消息或者任务。

简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。

2. 延迟队列使用场景

那么什么时候需要用延时队列呢?考虑一下以下场景:

  • 订单在十分钟之内未支付则自动取消。
  • 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
  • 账单在一周内未支付,则自动结算。
  • 用户注册成功后,如果三天内没有登陆则进行短信提醒。
  • 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
  • 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议。

3. 延迟队列的实现方式

3.1 利用TTL+死信队列

生产者生产一条延时消息,根据需要延时时间的不同,利用不同的routingkey将消息路由到不同的延时队列,每个队列都设置了不同的TTL属性,并绑定在同一个死信交换机中,消息过期后,根据routingkey的不同,又会被路由到不同的死信队列中,消费者只需要监听对应的死信队列进行处理即可。

rabbitTemplate发送延时消息_上传

这种方式的弊端,无法做到通用性,每搞一个新的延迟任务,都要去实现一个实现的TTL+死信队列,比较麻烦;

3.2 利用RabbitMQ插件实现

安装一个插件即可:Community Plugins

下载rabbitmq_delayed_message_exchange插件,然后解压放置到RabbitMQ的插件目录。

接下来,进入RabbitMQ的安装目录下的sbin目录,执行下面命令让该插件生效,然后重启RabbitMQ。

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

下面详细操作一下:

3.2.1 容器挂载

本地创建一个rabbitmq目录:

rabbitTemplate发送延时消息_java_02

执行下面命令:

docker run -it --rm --name rabbitmq5 -v /home/docker/rabbitmq/:/root/data/   -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin  -p 5672:5672 -p 15672:15672    rabbitmq:3-management
3.2.2 上传插件

插件下载地址: rabbitmq-delayed-message-exchange插件下载

然后直接通过finalshell上传rabbitmq_delayed_message_exchange插件到/home/docker/rabbitmq, 插件会自动同步到容器里的/root/data目录里面

rabbitTemplate发送延时消息_分布式_03

rabbitTemplate发送延时消息_分布式_04

3.2.3 复制到RabbitMQ的插件目录
cp /root/data/rabbitmq_delayed_message_exchange-3.8.0.ez /plugins

rabbitTemplate发送延时消息_上传_05

3.2.4 使插件生效

接下来,进入RabbitMQ的安装目录下的sbin目录,执行下面命令让该插件生效,然后重启RabbitMQ。

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

执行上面这个命令的时候有个报错说是版本不支持,又换了个rabbitmq_delayed_message_exchange-3.9.0.ez的,然后执行成功,如下图:

rabbitTemplate发送延时消息_分布式_06

然后通过 docker restart + 容器id 重启即可。

4. 代码实现

4.1 RabbitMQConfig配置

/**
     * delayedDirect交换机名称
     */
    public static final String DELAYED_DIRECT_EXCHANGE="delayedDirectExchange";

    /**
     * delayed direct队列名称
     */
    public static final String DELAYED_DIRECT_QUEUE="delayedDirectQueue";

    /**
     * delayed_direct路由Key
     */
    public static final String DELAYED_DIRECT_ROUTINGKEY="delayed_directRoutingKey";

    /**
     * 定义一个delayed direct交换机
     * @return
     */
    @Bean
    public CustomExchange delayedDirectExchange(){
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_DIRECT_EXCHANGE,"x-delayed-message", true, false, args);

    }

    /**
     * 定义一个DELAYED direct队列
     * @return
     */
    @Bean
    public Queue delayedDirectQueue(){
        return new Queue(DELAYED_DIRECT_QUEUE);
    }

    /**
     * 定义一个队列和交换机的绑定
     * @return
     */
    @Bean
    public Binding delayedDirectBinding(){
        return BindingBuilder.bind(delayedDirectQueue()).to(delayedDirectExchange()).with(DELAYED_DIRECT_ROUTINGKEY).noargs();
    }

4.2 producer

rabbitTemplate发送延时消息_上传_07

启动类:

rabbitTemplate发送延时消息_上传_08

4.3 consumer

rabbitTemplate发送延时消息_docker_09

另外,把手动签收注释掉,让它自动签收消息:

rabbitTemplate发送延时消息_上传_10

4.4 启动服务,测试

rabbitTemplate发送延时消息_java_11

rabbitTemplate发送延时消息_上传_12

测试成功!