RabbitMQ 死信队列和延时队列

RabbitMQ 死信队列和延时队列

TTL(Time To Live)

  • 消息的TTL就是消息的存活时间
  • RabbitMQ中对队列、消息都可以设置TTL
    • 对队列设置TTL,就是队列没有消费者连着的保留时间;对消息设置TTL,超过了这个时间,消息就死了,称之为死信。
    • 如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是—样的效果。

死信队列

概念

  • 死信:顾名思义就是无法被消费的消息,由于特殊的原因和条件下queue中的某些消息无法被消费,进而没有了后续处理变成了死信。

  • 死信Exchange:DLX,dead-letter-exchange

    • 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。DLX同时也是一个正常的交换机,它们可以像普通的交换机一样使用。
  • 死信路由route:队列中的消息过期后会被转发到这个路由键:x-dead-letter-routing-key

    • 普通的队列设置好自己的Dead Letter Exchange,当此队列中的消息过期后会被转发到这个路由,被称为死信路由。

可以进入死信路由的情况

  • TTL过期的消息
  • 被consumer拒收的消息,并且reject方法的参数里requeue是false(不会重新入队)
  • 队列消息满了,无法再添加数据到 mq 中,排在前面的消息会被丢弃或进入死信路由

延迟队列

概念

  • 延时队列,队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。

用途

  • 利用死信可以实现延迟队列。 比如设置队列有限期为60s,到期移动到另一个队列。比如订单有效支付时间30min,30min之后移动到另一个死信队列,我们可以监听另一个死信队列(例如可以根据唯一标识去查库,检验是否完成支付)。

代码实现

  • 基于RabbitMQ的延迟队列实现可以安装一个官方插件,该插件很好的集成了延迟队列。并且内部有优化机制。

    • 安装配置步骤

      # 1、官网下载 rabbitmq_delayed_message_exchange 插件
      # 下载直通车:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
      # 2、下载 rabbitmq_delayed_message_exchange-3.10.2.ez 文件
      # 3、上传至服务器,并且拷贝该文件到rabbitmq插件目录下或docker容器中(docker安装需要进入docker容器内启动)
      # 拷贝
      docker cp rabbitmq_delayed_message_exchange-3.9.0.ez  rabbitmq:/opt/rabbitmq/plugins/
      # 进入容器内部
      docker exec -it rabbitmq bash
      # 查看插件
      rabbitmq-plugins list
      # 开启死信交换机插件
      rabbitmq-plugins enable rabbitmq_delayed_message_exchange
      # 重启rabbitmq
      docker restart rabbitmq
      
  • 死信交换机和队列绑定关系

  • 创建死信交换机和死信队列Bean

    • 需要将队列和交换机注入容器中,并且在程序启动和自动配置时就加载。
    @Configuration
    public class DelayedQueueConfig {
    
        public static final String DELAYED_QUEUE_NAME = "delayed.queue";
        public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
        public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
    
        @Bean
        public Queue delayedQueue() {
            return  new Queue(DELAYED_QUEUE_NAME);
        }
    
        @Bean
        public CustomExchange delayedExchange() {
            HashMap<String, Object> args = new HashMap<>();
                args.put("x-delayed-type", "direct");
            return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
        }
    
        @Bean
        public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue,
                                           @Qualifier("delayedExchange") CustomExchange customExchange){
            return BindingBuilder.bind(queue).to(customExchange).with(DELAYED_ROUTING_KEY).noargs();
        }
    }
    
  • 生产者Controller编写

    @Slf4j
    @RequestMapping("ttl")
    @RestController
    public class SendMsgController {
    
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
    	// url: /ttl/sendDelayMsg/{message}/{delayTime}
        @GetMapping("sendDelayMsg/{message}/{delayTime}")
        public void sendMsg(@PathVariable String message, @PathVariable Integer delayTime) {
            rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAYED_ROUTING_KEY, message,
                    correlationData -> {
                        correlationData.getMessageProperties().setDelay(delayTime);
                        return correlationData;
                    });
            log.info("当前时间{}, 发送一条延迟{}毫秒的信息给队列 delayed.queue:{}",new Date(), delayTime, message);
        }
    }
    
  • 消费者组件编写

    @Slf4j
    @Component
    public class DeadLetterQueueConsumer {
        public static final String DELAYED_QUEUE_NAME = "delayed.queue";
        @RabbitListener(queues = DELAYED_QUEUE_NAME)
        public void receiveDelayedQueue(Message message) {
            String msg = new String(message.getBody());
            log.info("当前时间:{},收到延时队列的消息:{}", new Date().toString(), msg);
        }
    }
    
  • 测试

  • 配置文件

    spring.rabbitmq.host=192.168.197.133
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=guest
    spring.rabbitmq.password=guest
    server.port=8081
    
posted @ 2022-09-25 23:33  lam要努力  阅读(451)  评论(0编辑  收藏  举报