RabbitMQ的死信队列和延迟队列

死信队列

死信的概念

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

为了保证订单业务的消息数据不丢失,需要使用到 RabbitMQ 的死信队列机制,当消息消费发生异常时,将消息投入死信队列中。

死信产生的情况:

  • 消息 TTL 过期
  • 队列达到最大长度(队列满了,无法再添加数据到 mq 中)
  • 消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false

普通队列上配置死信交换机

死信队列是在普通队列的基础上的扩展,有死信队列就一定有死信交换机,只需要将普通队列和死信交换机进行“关联”,将处理不了的消息发送至死信交换机,再由死信交换机发送到与其绑定的队列上,就完成了死信队列的使用。重点部分:为普通队列配置死信交换机
在这里插入图片描述
普通队列上配置死信交换机的关键在于,队列声明时对arguments参数的配置。
arguments参数是一个Map集合。

public class Consumer {

    // 普通交换机
    private static final String NONE_EXCHANGE = "none_exchange";
    // 死信交换机
    private static final String DEAD_EXCHANGE = "dead_exchange";

    // 普通队列
    private static final String NONE_QUEUE = "none_queue";
    // 死信队列
    private static final String DEAD_QUEUE = "dead_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtils.getChannel();
        // 声明普通交换机和死信交换机,【正常的声明】
        channel.exchangeDeclare(NONE_EXCHANGE, BuiltinExchangeType.DIRECT);
        channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);

        // 声明死信队列,【正常的声明】
        channel.queueDeclare(DEAD_QUEUE, false, false, false, null);
        // 死信队列与死信交换机绑定,【正常的绑定】
        channel.queueBind(DEAD_QUEUE, 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");

        // 声明普通队列,将构建的参数传入
        channel.queueDeclare(NONE_QUEUE, false, false, false, params);
        // 普通队列与普通交换机绑定关系
        channel.queueBind(NONE_QUEUE, NONE_EXCHANGE, "zhangsan");

        System.out.println("等待接收消息...");

        channel.basicConsume(NONE_QUEUE, false, new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                long deliveryTag = message.getEnvelope().getDeliveryTag();
                if (deliveryTag == 5L) {
                    channel.basicReject(deliveryTag, false);
                } else {
                    System.out.println(new String(message.getBody(), StandardCharsets.UTF_8));
                    channel.basicAck(deliveryTag, false);
                }
            }
        }, new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        });
    }
}

消息进入死信队列的三种情况详解

消息 TTL 过期

消息TTL过期是由发送者发送消息时指定消息的ttl时间。
需要注意的是,消息是否过期是在即将发送给消费者前判断的。因为存储消息的数据结构是队列的形式,所以如果排在前面的消息没有处理完,即使当前消息已经过期,并不能立即进入死信队列。

public class Producer {

    private static final String NONE_EXCHANGE = "none_exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtils.getChannel();
        // 设置消息的TTL时间,消息过期后进入死信队列,expiration参数的单位毫秒
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().expiration("5000").build();
        for (int i = 0; i < 10; i++) {
            String msg = "消息:" + (i + 1);
            channel.basicPublish(NONE_EXCHANGE, "zhangsan", properties, msg.getBytes(StandardCharsets.UTF_8));
            System.out.println("生产者发送消息:" + msg);
        }
    }
}

队列达到最大长度

此效果可以将队列的长度设置的小点,来观察。

// 设置正常队列长度的限制
params.put("x-max-length", 6);

此参数需要在声明队列时传入,注意此时需要把原先队列删除 因为参数改变了。

测试

生产者发送10条消息。
在这里插入图片描述
在控制台可以看到,普通队列最大只能容纳6条消息,还有4条消息会被放入死信队列中。

消息被拒

消费者受用channel.basicReject和channel.basicNack拒收消息。这两个方法都有一个requeue参数,代表被拒收的消息收费重新入队,如果为true,消息会再次进入队列,然后再发送过来;如果为false代表拒绝重新入队该队列如果配置了死信交换机将发送到死信队列中

延迟队列

延迟队列概念

延迟队列,队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。
TTL 是 RabbitMQ 中一个消息或者队列的属性,表明一条消息或者该队列中的所有消息的最大存活时间,单位是毫秒。换句话说,如果一条消息设置了 TTL 属性或者进入了设置 TTL 属性的队列,那么这条消息如果在 TTL 设置的时间内没有被消费,则会成为"死信"。如果同时配置了队列的 TTL 和消息的
TTL,那么较小的那个值将会被使用,有两种方式设置 TTL

消息设置 TTL

针对每条消息设置ttl
在这里插入图片描述

队列设置 TTL

创建队列的时候设置队列的“x-message-ttl”属性
在这里插入图片描述

两者的区别

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

使用

就是在上述介绍的死信队列的基础上,为普通队列设置ttl过期时间
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值