06-RabbitMQ死信交换机

1.死信交换机介绍

当一个队列中信息满足下列情况之一时,可以成为死信(dead letter)

  &消费者使用basic.reject(RejectAndDontRequeueRecoverer处理策略)或bastic.nack(ImmediateRequeueMessageRecoverer处理策略)消费失败,requeue(重新入队)设置参数为false;

  &消息是一个过期消息,过期无人消费;

  &要投递的队列消息堆积满了,最早的消息成为死信;

如果该队列配置了dead-letter-exchange属性,指定交换机,队列设置dead-letter-routing-key属性,设置死信交换机与死信队列的RoutingKey,那么队列中的死信就会投递到这个交换机中,而这个交换机成为死信交换机(Dead Letter Exchange,简称DLX)。

 

 

 2.TTL

TTL,也就是Time-To-Live。如果队列中的消息TTL结束仍未消费则会成为死信,TTL超时分为两种情况:

  &消息所在的队列设置了存活时间;

  &消息本身设置了存活时间;

 

 

 3.代码实现

声明一组死信交换机和队列,注解模式:

@RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "dl.queue", durable = "true"),
            exchange = @Exchange(name = "dl.direct"),
            key = "dl"))
    public void listenDlQueue(String msg){
        log.info("接收到 dl.queue的延迟消息:{}", msg);
    }

要给队列设置超时时间,需要在声明队列时配置x-message-ttl属性:

  @Bean
    public Queue ttlQueue() {
        return QueueBuilder.durable("ttl.queue") //指定队列名称,并持久化
                .ttl(10000) //设置队列的超时时间,10秒
                .deadLetterExchange("dl.direct") //指定死信交换机
                .deadLetterRoutingKey("dl") //指定死信RoutingKey
                .build();
    }
 @Bean
    public Binding simpleBinding() {
        return BindingBuilder.bind(ttlQueue())
                .to(ttlExchange()).with("ttl");
    }

发送消息时给消息本身设置超时时间:

@Test
    public void testTTLMsg() {
        // 创建消息
        Message message = MessageBuilder
                .withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8))
                .setExpiration("5000")
                .build(); // 消息ID,需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息
        rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
    }

消息超时的两种方式:

  &给队列设置超时时间ttl,进入队列超过ttl的消息变为死信;

  &给消息设置超时时间,队列接受消息超过ttl时间未消费;

  &两者共存时,以ttl时间短的为准。

4..延迟队列

利用ttl结合死信交换机,实现了消息发出后,消费者延迟收到消息的结果,这种消息队列成为延迟队列(Delay Queue)模式。

延迟队列使用的场景:

  &用户下单后超过15分钟未支付取消支付;

  &预约20分钟会议,到时间自动通知参会人员;

延迟队列插件:RabbitMQ官方推出了DelayExchange插件

SpringAMQP使用延迟队列插件

DelayExchange的本质还是三种交换机,只是添加了延迟功能,因此使用时只需要声明一个交换机,交换机可以是任意类型,然后设定delayed属性为true即可。

基于注解方式实现:

@RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "delay.queue", durable = "true"),
            exchange = @Exchange(name = "delay.direct", delayed = "true"),
            key = "delay"))
    public void listenDlQueue(String msg) {
        log.info("接收到 delay.queue的延迟消息:{}", msg);
    }

然后我们向这个delay为true的交换机中发送消息,一定要给消息添加一个header:x-delay,值为延迟的时间,单位为毫秒:

 @Test
    public void testTTLMsg() {
        // 创建消息
        Message message = MessageBuilder
                .withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8))
                .setHeader("x-delay",10000)
                .build(); // 消息ID,需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息
        rabbitTemplate.convertAndSend("delay.direct", "ttl", message, correlationData);
    }

5.消息堆积问题

当生产者发送的消息的速度大于消费者消费的速度,就会导致队列中的消息堆积,直到达到消息的上限,最早接受的消息,可能会被丢弃,成为死信。

解决消费堆积的三种策略:

  &增加消费者,增加消费速度

  &在消费者内开启线程池加快消息处理速度

  &扩大队列的容积

6.惰性队列

从RabbitMq的3.6.0版本开始增加了Lazy Queues的概念,也就是惰性队列。

惰性队列的特征如下:

  &接受消息后直接存入磁盘而非内存;

  &消费者消费消息才会中磁盘中将消息加载到内存;

  &支持数百万的数据存储;

用StringAMQP声明队列的两种方式:

@Bean的方式:

 @Bean
    public Queue lazyQueue() {
        return QueueBuilder.durable("lazy.queue")
                .lazy()//开启惰性队列x-queue-mode
                .build();
    }

注解方式:

   @RabbitListener(queuesToDeclare = {
            @Queue(name = "lazy.queue",
                    declare = "true",
                    arguments = @Argument(name = "x-queue-mode", value = "lazy"))
    })
    public void listenLazyQueue(String msg) {
        System.out.println("消费者接收到lazy.queue的消息:【" + msg + "");
    }

 

posted @ 2023-04-12 19:25  老王的日常  阅读(91)  评论(0)    收藏  举报