RabbitMQ的死信队列详解

本文详细介绍了RabbitMQ中的死信队列(DLV)概念,包括其工作原理、死信产生的原因、如何设置和使用DLX。通过示例展示了如何在生产者和消费者间配置死信交换机,以及在Spring框架中的应用。演示了如何处理过期、拒收和队列满的死信消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.基础知识

1.死信队列介绍

  • 死信交换机:DLX,dead-letter-exchange
  • 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,进而推送到死信队列,这个Exchange就是DLX

2.消息变成死信有以下几种情况

3.死信处理过程

  • DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。
  • 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。
  • 可以监听这个队列中的消息做相应的处理。

4.死信队列设置

  • 首先需要设置死信的exchange和queue,然后进行绑定:
Exchange: dlx.exchange
Queue: dlx.queue
RoutingKey: #
#表示只要有消息到达了Exchange,那么都会路由到这个queue上
  • 然后需要有一个监听,去监听这个队列进行处理
  • 然后我们进行正常声明交换机、队列、绑定,只不过我们需要在正常队列(非死信队列)加上一个参数即可:arguments.put(" x-dead-letter-exchange","dlx.exchange");,将正常队列与死信交换机绑定,这样消息在过期、requeue、 队列在达到最大长度时,消息就可以直接路由到死信队列!

2.死信队列演示

1.前置信息:

1.死信交换机及队列: dlx.exchange、dlx.queue,路由key: #

2.测试交换机及队列: test_dlx_exchange、test_dlx_queue,路由key: dlx.save

2.生产端:

public class Producer {

    public static void main(String[] args) throws Exception {
        //1 创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.43.157");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        //2 获取Connection
        Connection connection = connectionFactory.newConnection();
        //3 通过Connection创建一个新的Channel
        Channel channel = connection.createChannel();
        
        String exchange = "test_dlx_exchange";
        String routingKey = "dlx.save";
        
        String msg = "Hello RabbitMQ DLX Message";
        
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2)
                .contentEncoding("UTF-8")
                .expiration("10000")
                .build();
        //发送消息
        channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
    }
}

自定义消费者:

public class MyConsumer extends DefaultConsumer {

    public MyConsumer(Channel channel) {
        super(channel);
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.err.println("-----------consume message----------");
        System.err.println("consumerTag: " + consumerTag);
        System.err.println("envelope: " + envelope);
        System.err.println("properties: " + properties);
        System.err.println("body: " + new String(body));
    }
}

3.消费端

  • 声明正常处理消息的交换机、队列及绑定规则
  • 在正常交换机上指定死信发送的Exchange
  • 声明死信交换机、队列及绑定规则
  • 监听死信队列,进行后续处理,这里省略
public class Consumer {

    public static void main(String[] args) throws Exception {
        
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.43.157");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        
        // 声明一个普通的交换机 和 队列 以及路由
        String exchangeName = "test_dlx_exchange";
        String routingKey = "dlx.#";
        String queueName = "test_dlx_queue";
        
        channel.exchangeDeclare(exchangeName, "topic", true, false, null);

        //绑定死信发送的Exchange 和 测试队列
        Map<String, Object> agruments = new HashMap<String, Object>();
        agruments.put("x-dead-letter-exchange", "dlx.exchange");
        //这个agruments属性,要设置到声明队列上
        channel.queueDeclare(queueName, true, false, false, agruments);

        //绑定测试交换机和测试队列
        channel.queueBind(queueName, exchangeName, routingKey);
        
        //要进行死信队列的声明
        channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
        channel.queueDeclare("dlx.queue", true, false, false, null);
        channel.queueBind("dlx.queue", "dlx.exchange", "#");
        
        channel.basicConsume(queueName, true, new MyConsumer(channel));
    }
}

在Spring中可以这样做:将普通队列与死信交换机绑定(死信交换机同时绑定了死信队列)

参考文章:RabbitMQ 消息有效期问题_天怎么不会塌的博客-CSDN博客_rabbitmq消息过期无效

@Configuration
public class DirectConfig {

    //普通交换机的名字
    public static final String MY_DIRECT_EXCHANGE_NAME = "my_direct_exchange_name";
    //普通队列的名字
    public static final String MY_DIRECT_QUEUE_NAME_01 = "my_direct_queue_name_01";

    //死信交换机的名字
    public static final String MY_DLX_DIRECT_EXCHANGE_NAME = "my_dlx_direct_exchange_name";
    //死信队列的名字
    public static final String MY_DLX_DIRECT_QUEUE_NAME_01 = "my_dlx_direct_queue_name_01";


    /**
     * 死信队列与死信交换机的连接
     * @return
     */
    @Bean
    Binding dlxBinding() {
        return BindingBuilder
                .bind(dlxQueue())
                .to(dlxDirectExchange())
                .with(MY_DLX_DIRECT_QUEUE_NAME_01);
    }

    /**
     * 死信队列
     * @return
     */
    @Bean
    Queue dlxQueue() {
        return new Queue(MY_DLX_DIRECT_QUEUE_NAME_01, true, false, false);
    }

    /**
     * 死信交换机
     * @return
     */
    @Bean
    DirectExchange dlxDirectExchange() {
        return new DirectExchange(MY_DLX_DIRECT_EXCHANGE_NAME, true, false);
    }

	//将普通队列与死信交换机绑定(死信交换机同时绑定了死信队列)
    @Bean
    Queue directQueue01() {
        Map<String, Object> args = new HashMap<>();
        //设置消息有效期,消息到期未被消费,就胡进入到死信交换机,并由死信交换机路由到死信队列
        args.put("x-message-ttl", 10000);
        //指定死信交换机
        args.put("x-dead-letter-exchange", MY_DLX_DIRECT_EXCHANGE_NAME);
        //指定死信队列
        args.put("x-dead-letter-routing-key", MY_DLX_DIRECT_QUEUE_NAME_01);
        return new Queue(MY_DIRECT_QUEUE_NAME_01, true, false, false, args);
    }

    
    @Bean
    DirectExchange directExchange() {
        return new DirectExchange(MY_DIRECT_EXCHANGE_NAME, true, false);
    }

    @Bean
    Binding directBinding01() {
        return BindingBuilder
                //指定要绑定的队列
                .bind(directQueue01())
                //指定交换机
                .to(directExchange())
                //就用队列名作为 rotingKey
                .with(MY_DIRECT_QUEUE_NAME_01);
    }
}

 

运行说明

启动消费端,此时查看管控台,新增了两个Exchange,两个Queue。在test_dlx_queue上我们设置了DLX,也就代表死信消息会发送到指定的死信交换机dlx.exchange上,最终其实会路由到死信队列dlx.queue上。

此时关闭消费端,然后启动生产端,查看管控台队列的消息情况,test_dlx_queue的值为1,而dlx_queue的值为0。
10s后的队列结果如图,由于生产端发送消息时指定了消息的过期时间为10s,而此时没有消费端进行消费(关闭了消费端),消息便被路由到死信队列中。

实际环境我们还需要对死信队列进行一个监听和处理,当然具体的处理逻辑和业务相关,这里只是简单演示死信队列是否生效。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值