当前位置: 首页 > 工具软件 > RabbitMQ-CN > 使用案例 >

RabbitMQ-Java-06-延迟队列

仲孙绍元
2023-12-01

说明

  • RabbitMQ-Java-06-延迟队列
  • 本案例是一个Maven+SpringBoot项目
  • 假设你已经实现了上一节死信队列
  • 官方文档已包含绝大多数本案例内容。请移步:https://docs.spring.io/spring-amqp/docs/current/reference/html/

核心概念

》延迟队列说明

  • 实现方式一:
    • 基于直接交换机的普通队列,给队列设置TTL
    • 该方式缺点:
      • 每个队列的TTL都是固定的,要想不同的TTL只能设置多个TTL,明显不符合我们的需求
  • 实现方式二:
    • 基于直接交换机的普通队列,消息发送方设置消息的TTL
    • 该方式缺点:
      • 多个不同TTL的消息发送到同一个队列,队列还是会依次执行消息,导致后边的TTL小的消息比他前边的TTL大的消息还要延后执行,明显不符合我们的需求
  • 实现方式三(重点掌握)
    • 基于插件:{rabbitmq_delayed_message_exchange},声明交换机的时候指定为{x-delayed-message}类型的交换机
    • 发消息的时候设置好消息特殊消息头:{x-delay}
      • 文档及下载地址:https://blog.rabbitmq.com/posts/2015/04/scheduling-messages-with-rabbitmq
      • 安装
        • 将下载的插件拷贝至RabbitMq插件目录(默认:/usr/lib/rabbitmq/plugins),如果没有就新建之
        • 开启插件:
          [admin@host001 plugins]$ pwd
          /usr/lib/rabbitmq/plugins
          
          # 重命名
          [admin@host001 plugins]$ sudo mv rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq_delayed_message_exchange.ez
          [admin@host001 plugins]$ ll
          total 36
          -rw-r--r--. 1 root root 36358 Dec 26 08:52 rabbitmq_delayed_message_exchange.ez
          
          # 开启
          [admin@host001 plugins]$ sudo rabbitmq-plugins enable rabbitmq_delayed_message_exchange
          Enabling plugins on node rabbit@host001:
          rabbitmq_delayed_message_exchange
          The following plugins have been configured:
            rabbitmq_delayed_message_exchange
            rabbitmq_management
            rabbitmq_management_agent
            rabbitmq_web_dispatch
          Applying plugin configuration to rabbit@host001...
          The following plugins have been enabled:
            rabbitmq_delayed_message_exchange
          
          started 1 plugins.
          
          # 重启RabbitMq
          [admin@host001 plugins]$ sudo service rabbitmq-server restart
          Redirecting to /bin/systemctl restart rabbitmq-server.service
          
          # 重新RabbitMq登录后台,新建交换机Type选项多了:x-delayed-message,表示插件开启成功
          
          
    • 该方式的优点:
      • 同一个队列可以接收任意delay的消息,处理消息的时候会根据消息的delay时间自动处理排序,nice~

》延迟队列实现过程概览

  • idea新建SpringBoot类型module
  • 添加Maven依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.76</version>
    </dependency>
    
    
  • 在默认配置文件中添加RabbitMq配置项
    spring.rabbitmq.host=192.168.3.202
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=admin
    spring.rabbitmq.password=123456
    
    
  • 开始搞事情

操作步骤

》实现方式一

  • 延迟队列自动配置类:DelayQueueConfig
    package cn.cnyasin.rabbit.config;
    
    import org.springframework.amqp.core.*;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * TTL队列自动配置类
     */
    @Configuration
    public class DelayQueueConfig {
        // 定义交换机名
        public static final String EXCHANGE_NORMAL = "exchange_normal";
        public static final String EXCHANGE_DEAD = "exchange_dead";
    
        // 定义队列名
        public static final String QUEUE_NORMAL_1 = "queue_normal_1";
        public static final String QUEUE_NORMAL_2 = "queue_normal_2";
        public static final String QUEUE_DEAD = "queue_dead";
    
        // 定义路由key
        public static final String ROUTING_NORMAL_1 = "routing_normal_1";
        public static final String ROUTING_NORMAL_2 = "routing_normal_2";
        public static final String ROUTING_DEAD = "routing_dead";
    
        // 声明交换机
        @Bean
        public DirectExchange ExchangeNormal() {
            return new DirectExchange(EXCHANGE_NORMAL);
        }
    
        @Bean
        public DirectExchange ExchangeDead() {
            return new DirectExchange(EXCHANGE_DEAD);
        }
    
        // 声明队列
        @Bean
        public Queue QueueNormal1() {
            Map<String, Object> arguments = new HashMap<>();
            arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
            arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
            arguments.put("x-message-ttl", 10000);
            return new Queue(QUEUE_NORMAL_1, true, false, false, arguments);
        }
    
        @Bean
        public Queue QueueNormal2() {
            Map<String, Object> arguments = new HashMap<>();
            arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
            arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
            arguments.put("x-message-ttl", 30000);
            return new Queue(QUEUE_NORMAL_2, true, false, false, arguments);
        }
    
        @Bean
        public Queue QueueDead() {
            return new Queue(QUEUE_DEAD, true, false, false, null);
        }
    
        // 绑定队列、交换机、路由key
        @Bean
        public Binding QueueNormal1BindingExchangeNormal(
                @Qualifier("QueueNormal1") Queue queueNormal1,
                @Qualifier("ExchangeNormal") Exchange exchangeNormal
        ) {
            return BindingBuilder.bind(queueNormal1).to(exchangeNormal).with(ROUTING_NORMAL_1).noargs();
        }
    
        @Bean
        public Binding QueueNormal2BindingExchangeNormal(
                @Qualifier("QueueNormal2") Queue queueNormal2,
                @Qualifier("ExchangeNormal") Exchange exchangeNormal
        ) {
            return BindingBuilder.bind(queueNormal2).to(exchangeNormal).with(ROUTING_NORMAL_2).noargs();
        }
    
        @Bean
        public Binding QueueDeadBindingExchangeDead(
                @Qualifier("QueueDead") Queue queueDead,
                @Qualifier("ExchangeDead") Exchange exchangeDead
        ) {
            return BindingBuilder.bind(queueDead).to(exchangeDead).with(ROUTING_DEAD).noargs();
        }
    
    }
    
    
  • 延迟队列消费者组件:DelayQueueConsumer
    package cn.cnyasin.rabbit.consumer;
    
    import cn.cnyasin.rabbit.config.DelayQueueConfig;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * 延迟队列消费者组件
     */
    @Slf4j
    @Component
    public class DelayQueueConsumer {
        /**
         * 队列TTL-消费者
         *
         * @param data
         */
        @RabbitListener(queues = DelayQueueConfig.QUEUE_DEAD)
        public void ddlQueueConsumer(String data) {
            log.info("[*] [{}] 死信队列接收到消息:{}", new Date().toString(), data);
        }
    }
    
    
  • 延迟队列控制器:DelayQueueController
    package cn.cnyasin.rabbit.controller;
    
    import cn.cnyasin.rabbit.config.DelayQueueConfig;
    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 延迟队列控制器
     */
    @Slf4j
    @RestController
    @RequestMapping("/delay/queue")
    public class DelayQueueController {
    
        @Autowired
        RabbitTemplate rabbitTemplate;
    
        /**
         * 队列TTL-生产者
         *
         * @param msg
         * @return
         */
        @RequestMapping("/ttl/queue/producer/{msg}")
        public String TtlQueueProducer(@PathVariable String msg) {
            log.info("[*] 准备发送消息:{}", msg);
    
            rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_1, msg);
            rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_2, msg);
    
            return JSON.toJSONString("消息发送成功");
        }
    }
    
    

》实现方式二

  • 延迟队列自动配置类:DelayQueueConfig(代码基于实现方式一,以下是新增的部分)
    // 定义队列名
    public static final String QUEUE_NORMAL_3 = "queue_normal_3";
    
    // 定义路由key
    public static final String ROUTING_NORMAL_3 = "routing_normal_3";
    
    
    @Bean
    public Queue QueueNormal3() {
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
        arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
        return new Queue(QUEUE_NORMAL_3, true, false, false, arguments);
    }
    
    @Bean
    public Binding QueueNormal3BindingExchangeNormal(
        @Qualifier("QueueNormal3") Queue queueNormal3,
        @Qualifier("ExchangeNormal") Exchange exchangeNormal
    ) {
        return BindingBuilder.bind(queueNormal3).to(exchangeNormal).with(ROUTING_NORMAL_3).noargs();
    }
    
  • 延迟队列控制器:DelayQueueController(代码基于实现方式一,以下是新增的部分)
    /**
     * 消息TTL-生产者
     *
     * @param msg
     * @return
     */
    @RequestMapping("/ttl/message/producer/{msg}/{ttl}")
    public String TtlMessageProducer(@PathVariable String msg, @PathVariable int ttl) throws Exception {
        log.info("[*] [{}]准备发送消息:{}", new Date().toString(), msg);
    
        Message message = MessageBuilder.withBody(msg.getBytes("UTF-8"))
            .setExpiration(String.valueOf(ttl))
            .build();
    
        rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_3, message);
    
        return JSON.toJSONString("消息发送成功");
    }
    

》实现方式三

  • 延迟队列自动配置类:DelayQueueConfig(代码基于实现方式一,以下是新增的部分)
    // 定义交换机名
    public static final String EXCHANGE_DELAY = "exchange_delay";
    // 定义队列名
    public static final String QUEUE_DELAY = "queue_delay";
    // 定义路由key
    public static final String ROUTING_DELAY = "routing_delay";
    
    // 声明交换机
    @Bean
    public CustomExchange ExchangeDelay() {
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-delayed-type", "direct");
        return new CustomExchange(EXCHANGE_DELAY, "x-delayed-message", true, false, arguments);
    }
    
    // 声明队列
    @Bean
    public Queue QueueDelay() {
        return new Queue(QUEUE_DELAY, true, false, false, null);
    }
    
    // 绑定队列、交换机、路由key
    @Bean
    public Binding QueueDelayBindingExchangeDelay(
        @Qualifier("QueueDelay") Queue queueDelay,
        @Qualifier("ExchangeDelay") Exchange exchangeDelay
    ) {
        return BindingBuilder.bind(queueDelay).to(exchangeDelay).with(ROUTING_DELAY).noargs();
    }
    
    
  • 延迟队列消费者组件:DelayQueueConsumer(代码基于实现方式一,以下是新增的部分)
    /**
     * delay队列-消费者
     *
     * @param data
     */
    @RabbitListener(queues = DelayQueueConfig.QUEUE_DELAY)
    public void delayQueueConsumer(String data) {
        log.info("[*] [{}] 延迟队列接收到消息:{}", new Date().toString(), data);
    }
    
    
  • 延迟队列控制器:DelayQueueController(代码基于实现方式一,以下是新增的部分)
    /**
     * delay消息-生产者
     *
     * @param msg
     * @return
     */
    @RequestMapping("/delay/message/producer/{msg}/{ttl}")
    public String DelayMessageProducer(@PathVariable String msg, @PathVariable int ttl) throws Exception {
        log.info("[*] [{}]准备发送消息:{}", new Date().toString(), msg);
    
        Message message = MessageBuilder.withBody(msg.getBytes("UTF-8"))
                .setHeader("x-delay", String.valueOf(ttl))
                .build();
    
        rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_DELAY, DelayQueueConfig.ROUTING_DELAY, message);
    
        return JSON.toJSONString("消息发送成功");
    }
    
    

备注

  • 该教程部分内容收集自网络,感谢原作者。

附录

 类似资料: