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 ,则表示除非此时可以 直接投递该消息到消费者,否则该消息将会被丢弃。

相关推荐
路在脚下@40 分钟前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien43 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0073 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生3 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss3 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星3 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言