rabbitmq死信队列和延迟队列

死信队列

死信队列是什么?

首先死信队列中消息不能在规定的条件下无法发送给消费者, 到转发到死信队列中的整个过程

消息变成死信消息的条件

  1. TTL 超时
  2. 消息队列满了, 无法继续添加
  3. 消息被拒绝

死信队列应该怎么设计?

K%U6G@QM96F0XB724FO.jpg

那么死信队列的代码应该怎么写?

可以根据上面的图片照着写就成

 /**
  * 正常消费者
  */
 public class Consumer1 {
    public static final String EXCHANGE = "exchange";
    public static final String DEAD_EXCHANGE = "dead_exchange";
    public static final String QUEUE = "queue";
    public static final String DEAD_QUEUE = "dead_queue";
    public static final String DEAD_ROUTING_KEY = "dead_routing_key";
    
    public static void main(String[] args) throws Exception {
       ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
       Connection connection = factory.newConnection();
       Channel channel = connection.createChannel();
       
       channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
       HashMap<String, Object> map = new HashMap<>();
       // 消息过期时间
       map.put("x-message-ttl", 10000);
       // queue消息触发三个死信条件后, 将消息提交给哪个 exchange
       map.put("x-dead-letter-exchange", DEAD_EXCHANGE);
       // 跟死信交换机绑定的死信routing key
       map.put("x-dead-letter-routing-key", DEAD_ROUTING_KEY);
       channel.queueDeclare(QUEUE, true, false, false, map);
       // 正常的交换机和正常队列绑定
       channel.queueBind(QUEUE, EXCHANGE, "");
       // 死信交换机的定义
       channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT, true);
       // 定义死信队列
       channel.queueDeclare(DEAD_QUEUE, true, false, false, null);
       // 绑定死信交换机和死信队列和死信routing key
       channel.queueBind(DEAD_QUEUE, DEAD_EXCHANGE, DEAD_ROUTING_KEY);
       
       channel.basicConsume(QUEUE, false, (consumerTag, message) -> {
          if (message.getEnvelope().getDeliveryTag() % 3 == 0L) {
             // 模拟消息被拒绝
             channel.basicReject(message.getEnvelope().getDeliveryTag(), false);
             return;
          }
          System.err.println("消息id: " + consumerTag + ", 消息内容: " + new String(message.getBody(), Charset.defaultCharset())
                + ", 累计发送了: " + message.getEnvelope().getDeliveryTag() + "个消息");
       }, System.err::println);
    }
 }
 /**
  * 死信消费者
  */
 public class Consumer2 {
    
    public static final String DEAD_QUEUE = "dead_queue";
    
    public static void main(String[] args) throws Exception {
       ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
       Connection connection = factory.newConnection();
       Channel channel = connection.createChannel();
       channel.basicConsume(DEAD_QUEUE, false, (consumerTag, message) -> {
          System.err.println("消息id: " + consumerTag + ", 消息内容: " + new String(message.getBody(), Charset.defaultCharset()) +
                ", 累计发送了: " + message.getEnvelope().getDeliveryTag() + "个消息");
          channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
       }, System.err::println);
    }
    
 }
 public class Producer {
    
    public static final String EXCHANGE = "exchange";
    
    public static void main(String[] args) throws Exception {
       ConcurrentSkipListMap<Long, String> map = new ConcurrentSkipListMap<>();
       
       ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
       Connection connection = factory.newConnection();
       Channel channel = connection.createChannel();
       
       channel.confirmSelect();
       
       channel.addConfirmListener(new ConfirmListener() {
          @Override
          public void handleAck(long deliveryTag, boolean multiple) throws IOException {
             if (multiple) {
                ConcurrentNavigableMap<Long, String> headMap = map.headMap(deliveryTag);
                headMap.clear();
             } else {
                map.remove(deliveryTag);
             }
          }
          
          @Override
          public void handleNack(long deliveryTag, boolean multiple) throws IOException {
             System.err.println("消息: " + map.get(deliveryTag) + ", 是否批处理: " + multiple);
             handleAck(deliveryTag,multiple);
          }
       });
       
       AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().expiration("10000").build();
       for (int i = 0; i < 100; i++) {
          String message = "message" + i;
          map.put(channel.getNextPublishSeqNo(), message);
          channel.basicPublish(EXCHANGE, "", basicProperties, message.getBytes(Charset.defaultCharset()));
       }
    }
 }

代码过长, 最好别看, 直接看图, 你就能写出代码

TTL超时时间

消息和queue都可以配置超时时间

消费者

 rabbitTemplate.convertAndSend(DELAY_EXCHANGE, "", message,
       m -> {
          m.getMessageProperties().setDelay(delayTime);
          return m;
       });

配置延迟时间就可以

生产者

 @Bean
 public Queue dlxQueue() {
    return QueueBuilder.durable(DELAY_QUEUE)
          .ttl(3000) // 延迟 3 秒
          .autoDelete()
          .build();
 }

对管道做延迟, 延迟3秒, 这个3秒是针对消息的, 不是队列的哦

延迟队列如何实现?

这里有两种方法

  1. 通过DLX
  2. 通过延迟插件

通过DLX

延迟队列给予死信队列, 将正常的消费者删除掉, 只留下死信消费者

T

Producer:

ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();

channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT, true, true, null);

channel.confirmSelect();
channel.addConfirmListener((deliveryTag, multiple) -> {
   String dateTime = DatePattern.NORM_DATETIME_FORMAT.format(new Date());
   System.err.println(deliveryTag + ": 消息应答" + " '发送'时间为: " + dateTime);
}, (deliveryTag, multiple) -> System.err.println(deliveryTag + ": 消息未得到应答"));

for (int i = 0; i < 10; i++) {
   String message = UUID.randomUUID().toString() + i;
   channel.basicPublish(NORMAL_EXCHANGE,"",
         MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(Charset.defaultCharset()));
}

Consumer:

public static final String NORMAL_EXCHANGE = "exchange";
public static final String NORMAL_QUEUE = "queue";
public static final String DELAY_EXCHANGE = "delay_exchange";
public static final String DELAY_ROUTING_KEY = "routing_key_name";
public static final String DELAY_QUEUE = "delay_queue";

public static void main(String[] args) throws Exception {
   ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
   Connection connection = factory.newConnection();
   Channel channel = connection.createChannel();
   
   channel.exchangeDeclare(DELAY_EXCHANGE, BuiltinExchangeType.DIRECT, true, true, null);
   channel.queueDeclare(DELAY_QUEUE, true, false, true, null);
   channel.queueBind(DELAY_QUEUE, DELAY_EXCHANGE, DELAY_ROUTING_KEY);
   
   channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT, true, true, null);
   var builder = QueueBuilder.durable(DELAY_QUEUE)
         .deadLetterRoutingKey(DELAY_ROUTING_KEY)
         .deadLetterExchange(DELAY_EXCHANGE).build();
   channel.queueDeclare(NORMAL_QUEUE, true, false, true, builder.getArguments());
   channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "");
   
   DeliverCallback deliverCallback = (consumerTag, message) -> System.err.println("消息接收时间: "
         + DatePattern.NORM_DATETIME_FORMAT.format(new Date())
         + " consumerTag: " + consumerTag + " message: " + message);
   CancelCallback cancelCallback = System.err::println;
   channel.basicConsume(DELAY_QUEUE, false, deliverCallback, cancelCallback);
}

这种方式实现延迟队列的问题

比如 A 需要延迟 5s 后执行消息, B 需要 10s 后执行消息, C 需要 30s 后执行消息

那么将会有三个queue

image.png

问: 那么有无限中延迟需求呢?

答: 专门给出一个队列, 消费者往该队列发送存在带过期时间的消息

image.png

消费者给发送的消息添加延迟时间

 public static final String NORMAL_EXCHANGE = "exchange";
 ​
 public static void main(String[] args) throws Exception {
    ConnectionFactory factory = RabbitUtils.INSTANCE.connectionFactory();
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    
    channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT, true, true, null);
    
    channel.confirmSelect();
    channel.addConfirmListener((deliveryTag, multiple) -> {
       String dateTime = DatePattern.NORM_DATETIME_FORMAT.format(new Date());
       System.err.println(deliveryTag + ": 消息应答" + " '发送'时间为: " + dateTime);
    }, (deliveryTag, multiple) -> System.err.println(deliveryTag + ": 消息未得到应答"));
    
    AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
    builder.expiration("10000");
    for (int i = 0; i < 10; i++) {
       String message = UUID.randomUUID().toString() + i;
       channel.basicPublish(NORMAL_EXCHANGE,"",
             builder.build(), message.getBytes(Charset.defaultCharset()));
    }
 }

插件延迟队列需要整合springboot才会比较方便使用, 将会在下一章节讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值