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

相关推荐
薛纪克几秒前
Lambda Query:让微软Dataverse查询像“说话”一样简单
java·spring·microsoft·lambda·dataverse
程序员-周李斌2 分钟前
CopyOnWriteArrayList 源码分析
java·开发语言·哈希算法·散列表
廋到被风吹走3 分钟前
【Spring】两大核心基石 IoC和 AOP
java·spring
明有所思9 分钟前
springsecurity更换加密方式
java·spring
却话巴山夜雨时i15 分钟前
295. 数据流的中位数【困难】
java·服务器·前端
java干货22 分钟前
优雅停机!Spring Boot 应用如何使用 Hook 线程完成“身后事”?
java·spring boot·后端
tealcwu25 分钟前
【Unity技巧】实现在Play时自动保存当前场景
java·unity·游戏引擎
uup25 分钟前
Java 多线程下的可见性问题
java
用户83071968408225 分钟前
通过泛型限制集合只读或只写
java