rabbitmq的死信队列

目录

成为死信的条件

消息TTL过期

队列达到最大长度

消息被拒

延迟队列

延迟队列使用场景

[消息设置 TTL](#消息设置 TTL)

[队列设置 TTL](#队列设置 TTL)

两者区别


producer 将消息投递到 broker 或者直接到 queue 里了, consumer 从 queue 取出消息
进行消费,但某些时候由于特定的 原因导致 queue 中的某些消息无法被消费 ,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有了死信队列。

成为死信的条件

  1. 超过消息的存活时间(TTL):可以为消息设置一个存活时间,在该时间段之后,如果消息还未被消费或者被重新投递到其他队列,该消息将成为死信。

  2. 消息被拒绝(Reject):当消息被消费者拒绝接收时,可以选择将该消息重新投递到另一个队列或将其标记为死信。

  3. 消息达到最大重试次数:可以通过在消费者端设置重试次数限制,当消息达到一定的重试次数而仍然无法被消费时,该消息将成为死信。

  4. 队列满溢(Queue Overflow):当一个队列的消息数量已经超过队列的最大容量限制时,新消息无法进入队列,而被视为死信。

消息TTL过期

生产者

java 复制代码
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
      private static final String NORMAL_EXCHANGE = "normal_exchange";

      public static void main(String[] argv) throws Exception {
      try (Channel channel = RabbitMqUtils.getChannel()) {
      channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
        //设置消息的 TTL 时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
        //该信息是用作演示队列个数限制
        for (int i = 1; i <11 ; i++) {
          String message="info"+i;
          channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties,
          message.getBytes());
          System.out.println("生产者发送消息:"+message);
        }
      }
   }
}

消费者1

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.util.HashMap;
import java.util.Map;

public class Consumer01 {
       //普通交换机名称
       private static final String NORMAL_EXCHANGE = "normal_exchange";
       //死信交换机名称
       private static final String DEAD_EXCHANGE = "dead_exchange";
       public static void main(String[] argv) throws Exception {
           Channel channel = RabbitMqUtils.getChannel();
           //声明死信和普通交换机 类型为 direct
           channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
           channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
           //声明死信队列
           String deadQueue = "dead-queue";
           channel.queueDeclare(deadQueue, false, false, false, null);
           //死信队列绑定死信交换机与 routingkey
           channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
           //正常队列绑定死信队列信息
           Map<String, Object> params = new HashMap<>();
           //正常队列设置死信交换机 参数 key 是固定值
           params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
           //正常队列设置死信 routing-key 参数 key 是固定值
           params.put("x-dead-letter-routing-key", "lisi");

           String normalQueue = "normal-queue";
           channel.queueDeclare(normalQueue, false, false, false, params);
           channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
           System.out.println("等待接收消息.....");
           DeliverCallback deliverCallback = (consumerTag, delivery) -> {
              String message = new String(delivery.getBody(), "UTF-8");
              System.out.println("Consumer01 接收到消息"+message);
           };
           channel.basicConsume(normalQueue, true, deliverCallback, consumerTag -> {
           });
       }
}

消费者2

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
      Channel channel = RabbitMqUtils.getChannel();
      channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
      String deadQueue = "dead-queue";
      channel.queueDeclare(deadQueue, false, false, false, null);
      channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
      System.out.println("等待接收死信队列消息.....");
      DeliverCallback deliverCallback = (consumerTag, delivery) -> {
         String message = new String(delivery.getBody(), "UTF-8");
         System.out.println("Consumer02 接收死信队列的消息" + message);
      };
      channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
      });
      }
}

关闭消费者1,模拟出故障

正常队列

消息超时进入死信队列

死信队列接收信息

队列达到最大长度

生产者

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     public static void main(String[] argv) throws Exception {
         try (Channel channel = RabbitMqUtils.getChannel()) {
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         //该信息是用作演示队列个数限制
         for (int i = 1; i <11 ; i++) {
         String message="info"+i;
         channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",null, message.getBytes());
         System.out.println("生产者发送消息:"+message);
         }
         }
     }
}

消费者1

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.util.HashMap;
import java.util.Map;

public class Consumer01 {
       //普通交换机名称
       private static final String NORMAL_EXCHANGE = "normal_exchange";
       //死信交换机名称
       private static final String DEAD_EXCHANGE = "dead_exchange";

       public static void main(String[] argv) throws Exception {
           Channel channel = RabbitMqUtils.getChannel();
           //声明死信和普通交换机 类型为 direct
           channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
           channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
           //声明死信队列
           String deadQueue = "dead-queue";
           channel.queueDeclare(deadQueue, false, false, false, null);
           //死信队列绑定死信交换机与 routingkey
           channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
           //正常队列绑定死信队列信息
           Map<String, Object> params = new HashMap<>();
           //正常队列设置死信交换机 参数 key 是固定值
           params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
           //正常队列设置死信 routing-key 参数 key 是固定值
           params.put("x-dead-letter-routing-key", "lisi");
           // 设置正常队列长度的限制
           params.put("x-max-length",6);

           String normalQueue = "normal-queue";
           channel.queueDeclare(normalQueue, false, false, false, params);
           channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
           System.out.println("等待接收消息.....");
           DeliverCallback deliverCallback = (consumerTag, delivery) -> {
              String message = new String(delivery.getBody(), "UTF-8");
              System.out.println("Consumer01 接收到消息"+message);
           };
           channel.basicConsume(normalQueue, true, deliverCallback, consumerTag -> {
           });
       }
}

消费者2

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
            Channel channel = RabbitMqUtils.getChannel();
            channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
            String deadQueue = "dead-queue";
            channel.queueDeclare(deadQueue, false, false, false, null);
            channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
            System.out.println("等待接收死信队列消息.....");
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
               String message = new String(delivery.getBody(), "UTF-8");
               System.out.println("Consumer02 接收死信队列的消息" + message);
            };
            channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
            });
      }
}

关闭消费者1,模拟接收不到信息

死信队列消费了四个消息

分析:生产者产生10个消息,正常队列只能接受6个消息,多的消息便被转移到死信队列去了

消息被拒

生产者

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     public static void main(String[] argv) throws Exception {
         try (Channel channel = RabbitMqUtils.getChannel()) {
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         //该信息是用作演示队列个数限制
         for (int i = 1; i <11 ; i++) {
         String message="info"+i;
         channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",null, message.getBytes());
         System.out.println("生产者发送消息:"+message);
         }
         }
     }
}

消费者1

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.util.HashMap;
import java.util.Map;

public class Consumer01 {
     //普通交换机名称
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     //死信交换机名称
     private static final String DEAD_EXCHANGE = "dead_exchange";
     public static void main(String[] argv) throws Exception {
         Channel channel = RabbitMqUtils.getChannel();
         //声明死信和普通交换机 类型为 direct
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
         //声明死信队列
         String deadQueue = "dead-queue";
         channel.queueDeclare(deadQueue, false, false, false, null);
         //死信队列绑定死信交换机与 routingkey
         channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
         //正常队列绑定死信队列信息
         Map<String, Object> params = new HashMap<>();
         //正常队列设置死信交换机 参数 key 是固定值
         params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
         //正常队列设置死信 routing-key 参数 key 是固定值
         params.put("x-dead-letter-routing-key", "lisi");
         String normalQueue = "normal-queue";
         channel.queueDeclare(normalQueue, false, false, false, params);
         channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
         System.out.println("等待接收消息.....");
         DeliverCallback deliverCallback = (consumerTag, delivery) -> {
             String message = new String(delivery.getBody(), "UTF-8");
             if(message.equals("info5")){
                 System.out.println("Consumer01 接收到消息" + message + "并拒绝签收该消息");
                 //requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中
                 channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);
             }else {
                 System.out.println("Consumer01 接收到消息"+message);
                 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
             }
         };
         boolean autoAck = false;
         channel.basicConsume(normalQueue, autoAck, deliverCallback, consumerTag -> {
         });
     }
}

消费者2

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
            Channel channel = RabbitMqUtils.getChannel();
            channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
            String deadQueue = "dead-queue";
            channel.queueDeclare(deadQueue, false, false, false, null);
            channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
            System.out.println("等待接收死信队列消息.....");
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
               String message = new String(delivery.getBody(), "UTF-8");
               System.out.println("Consumer02 接收死信队列的消息" + message);
            };
            channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
            });
      }
}

结果

消费者1,拒绝接受消息info5,info5进入死信队列

消费者2,死信队列接受到info5

延迟队列

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

延迟队列使用场景

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

消息设置 TTL

java 复制代码
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
      private static final String NORMAL_EXCHANGE = "normal_exchange";

      public static void main(String[] argv) throws Exception {
      try (Channel channel = RabbitMqUtils.getChannel()) {
      channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
        //设置消息的 TTL 时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
        //该信息是用作演示队列个数限制
        for (int i = 1; i <11 ; i++) {
          String message="info"+i;
          channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties,
          message.getBytes());
          System.out.println("生产者发送消息:"+message);
        }
      }
   }
}

队列设置 TTL

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.util.HashMap;
import java.util.Map;

public class Consumer01 {
     //普通交换机名称
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     //死信交换机名称
     private static final String DEAD_EXCHANGE = "dead_exchange";
     public static void main(String[] argv) throws Exception {
         Channel channel = RabbitMqUtils.getChannel();
         //声明死信和普通交换机 类型为 direct
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
         //声明死信队列
         String deadQueue = "dead-queue";
         channel.queueDeclare(deadQueue, false, false, false, null);
         //死信队列绑定死信交换机与 routingkey
         channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
         //正常队列绑定死信队列信息
         Map<String, Object> params = new HashMap<>();
         //正常队列设置死信交换机 参数 key 是固定值
         params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
         //正常队列设置死信 routing-key 参数 key 是固定值
         params.put("x-dead-letter-routing-key", "lisi");
         // 设置 TTL 值为 5000 毫秒(5 秒)
         params.put("x-message-ttl", 5000);
         String normalQueue = "normal-queue";
         channel.queueDeclare(normalQueue, false, false, false, params);
         channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
         System.out.println("等待接收消息.....");
         DeliverCallback deliverCallback = (consumerTag, delivery) -> {
             String message = new String(delivery.getBody(), "UTF-8");
             if(message.equals("info5")){
                 System.out.println("Consumer01 接收到消息" + message + "并拒绝签收该消息");
                 //requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中
                 channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);
             }else {
                 System.out.println("Consumer01 接收到消息"+message);
                 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
             }
         };
         boolean autoAck = false;
         channel.basicConsume(normalQueue, autoAck, deliverCallback, consumerTag -> {
         });
     }
}

两者区别

如果设置了队列的 TTL 属性,那么一旦消息过期,就会被队列丢弃 ( 如果配置了死信队列被丢到死信队列中) ,而第二种方式,消息即使过期,也不一定会被马上丢弃,因为 消息是否过期是在即将投递到消费者 之前判定的 ,如果当前队列有严重的消息积压情况,则已过期的消息也许还能存活较长时间;另外,还需 要注意的一点是,如果不设置 TTL ,表示消息永远不会过期,如果将 TTL 设置为 0 ,则表示除非此时可以 直接投递该消息到消费者,否则该消息将会被丢弃。

相关推荐
乌啼霜满天2492 分钟前
JDBC编程---Java
java·开发语言·sql
色空大师14 分钟前
23种设计模式
java·开发语言·设计模式
闲人一枚(学习中)15 分钟前
设计模式-创建型-建造者模式
java·设计模式·建造者模式
2202_7544215433 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
蓝染-惣右介35 分钟前
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
java·数据库·tomcat·mybatis
小林想被监督学习36 分钟前
idea怎么打开两个窗口,运行两个项目
java·ide·intellij-idea
HoneyMoose38 分钟前
IDEA 2024.3 版本更新主要功能介绍
java·ide·intellij-idea
我只会发热39 分钟前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
是老余41 分钟前
本地可运行,jar包运行错误【解决实例】:通过IDEA的maven package打包多模块项目
java·maven·intellij-idea·jar
crazy_wsp41 分钟前
IDEA怎么定位java类所用maven依赖版本及引用位置
java·maven·intellij-idea