RabbitMQ死信交换永远不会得到消息

时间:2014-02-12 23:32:44

标签: rabbitmq dead-letter

我正在尝试设置我的第一个RabbitMQ死信交换,这是我通过网络管理界面使用的步骤:

  1. 使用名称“dead.letter.test”
  2. 创建新的DIRECT交换
  3. 创建新队列“dead.letter.queue”
  4. 将“dead.letter.queue”绑定到“dead.letter.test”
  5. 创建新队列“test1”,将死信交换设置为“dead.letter.test”
  6. 将消息发送到“test1”
  7. Nack(requeue = false)“test1”中的消息
  8. 我期待这些步骤应该通过“dead.letter.test”交换记录到“dead.letter.queue”。这不会发生。

    我可以手动将消息放入“dead.letter.test”交换中,它显示在“dead.letter.queue”中,所以我知道这很好。

    当我查看管理界面时,它显示在队列“test1”上设置了DLX参数。

    我哪里错了?

8 个答案:

答案 0 :(得分:55)

Gentilissimo Signore非常友好地在Twitter上回答我的问题。问题是如果您的死信交换设置为DIRECT,您必须指定死信路由密钥。如果您只是希望所有NACKed消息进入一个死信桶以供以后调查(就像我一样)那么您的死信交换应设置为FANOUT。

以下是有效的更新步骤:

  1. 使用名称“dead.letter.test”创建新的 FANOUT 交换
  2. 创建新队列“dead.letter.queue”
  3. 将“dead.letter.queue”绑定到“dead.letter.test”
  4. 创建新队列“test1”,将死信交换设置为“dead.letter.test”
  5. 将消息发送到“test1”
  6. Nack(requeue = false)“test1”中的消息

答案 1 :(得分:11)

没有路由密钥和直接交换的死信交换


按照以下步骤进行操作: -  1.创建名为“ dead_queue ”的新队列。
 2.创建名为“ dead_exchange ”的交易所,交易类型应为“直接”  3.在没有路由键的情况下绑定' dead_queue '和' dead_exchange '。
 4.创建一个名为“ test_queue ”的新队列,并将其“ x-dead-letter-exchange ”名称设置为“ dead_exchange
 5.创建名为“ test_exchange ”的交易所,交易类型应为“直接”  6.在没有路由密钥的情况下绑定' test_exchange '和' test_queue '。

最后我们会检查它。为此,在' test_exchange '上发布一些参数' expiration '设置为10000的内容。在此之后,当消息在' test_exchange '上发布时将转到' test_queue ',当消息在队列中过期时,它将查找DLX参数(死信交换名称),该消息找到名称' dead_exchange '然后该消息将到达' dead_exchange '将其传递给'死队列'.. 如果你仍然有任何问题,如果我错过了解你的问题...写你的问题我一定会看看它...谢谢..

注意:必须在' test_exchange '上发布消息,因为test_queue和test_exchange绑定没有路由密钥,它可以正常工作但如果你在'<上发布消息b> test_queue 将使用默认的交换和路由密钥。然后,在消息队列到期后尝试使用某个默认路由密钥将该死信息传递给dead_exchange,并且消息将不会进入该队列。

答案 2 :(得分:6)

如果要在死信交换上使用自定义路由密钥,则必须在声明工作队列时设置x-dead-letter-routing-key(在您的情况下为test1),否则将使用默认路由密钥。在您的情况下,RabbitMQ代理检测循环并简单地丢弃被拒绝的消息。

您需要在x-dead-letter-exchange=dead.letter.test队列上设置x-dead-letter-routing-key=dead.letter.queuetest1个参数。

答案 3 :(得分:3)

如果您希望所有队列都有相同的死信交换,则更容易设置一般政策:

sudo rabbitmqctl -p /my/vhost/path set_policy DLX ".*" '{"dead-letter-exchange":"MyExchange.DEAD"}' --apply-to queues

答案 4 :(得分:3)

如果不是强制性的,则不需要创建FANOUT交换。

您可以使用与其他交换相同的路由密钥来创建DIRECT交换。并且也不需要为新的交换创建新的队列。您可以将现有队列与新交换一起使用。您只需要将该新交换与队列绑定即可。

这是我的receive.js文件:

var amqp = require("amqplib/callback_api");
var crontab = require('node-crontab');

amqp.connect("amqp://localhost", function (err, conn) {
conn.createChannel(function (err, ch) {
    var ex = 'direct_logs';
    var ex2 = 'dead-letter-test';
    var severity = 'enterprise-1-key';

    //assert "direct" exchange
    ch.assertExchange(ex, 'direct', { durable: true });
    //assert "dead-letter-test" exchange
    ch.assertExchange(ex2, 'direct', { durable: true });

    //if acknowledgement is nack() then message will be stored in second exchange i.e. ex2="dead-letter-test"
    ch.assertQueue('enterprise-11', { exclusive: false, deadLetterExchange: ex2 }, function (err, q) {
        var n = 0;
        console.log(' [*] Waiting for logs. To exit press CTRL+C');
        console.log(q);

        //Binding queue with "direct_logs" exchange
        ch.bindQueue(q.queue, ex, severity);
        //Binding the same queue with "dead-letter-test"
        ch.bindQueue(q.queue, ex2, severity);

        ch.consume(q.queue, function (msg) {
            // consume messages via "dead-letter-exchange" exchange at every second.
            if (msg.fields.exchange === ex2) {
                crontab.scheduleJob("* * * * * *", function () {
                    console.log("Received by latest exchange %s", msg.fields.routingKey, msg.content.toString());
                });
            } else {
                console.log("Received %s", msg.fields.routingKey, msg.content.toString());
            }

            if (n < 1) {
                // this will executes first time only. Here I'm sending nack() so message will be stored in "deadLetterExchange"
                ch.nack(msg, false, false);
                n += 1;
            } else {
                ch.ack(msg)
                n = 0
            }
        }, { noAck: false });
    });
  });
});

答案 5 :(得分:2)

  

使用名称“dead.letter.test”

创建新的DIRECT交换

正确

  

创建新队列“dead.letter.queue”

正确

  

将“dead.letter.queue”绑定到“dead.letter.test”

正确

  

创建新队列“test1”,将死信交换设置为“dead.letter.test”

我假设您正在创建test1队列并将其绑定到dead.letter.test exchange

  

发送消息到“test1”

如果您希望dead.letter.queue收到您的邮件,则必须在发送邮件时提供路由密钥,而使用dead.letter.queue的客户端也应使用相同的路由密钥

如果您在没有路由密钥的情况下发布,那么只有订阅了test1的客户才会收到该消息。

如果您将消息发布到direct.letter.test exchange,则所有队列都将收到该消息。它会像扇出交换一样工作

因此,如果您希望dead.letter.queue接收消息,您将不得不在该队列中发布消息,或者您必须在发布和订阅时使用相同的路由密钥并将消息发布到交换

答案 6 :(得分:0)

在我的情况下,问题是因为队列中有

ackMode="MANUAL"

但是我从来没有设置它(因为运行时异常),请改用Defaul ACK。

答案 7 :(得分:0)

对于那些使用 Spring-AMQP 的人

就我而言,问题有所不同。我想要一个直接类型的死信交换。我为队列设置了 x-dead-letter-exchangex-dead-letter-routing-key。另外,我在 spring.rabbitmq.listener.simple.default-requeue-rejected=false 中有 application.properties

看起来一切正常,但在调试时我注意到我的 SimpleRabbitListenerContainerFactorydefaultRequeueRejected 为空。所以原因是当您在 SimpleRabbitListenerContainerFactory 中声明 @Configuration 时,您创建了一个新的“非默认”bean。默认的一个是在你的属性中为你创建的。但是你的SimpleRabbitListenerContainerFactory中的@Config,这些属性是不读的,必须自己读,在java代码中设置。

它发生在我身上,因为当我想配置并发时,我只是从 the Spring-AMQP docs 复制粘贴了配置。但是你应该在一个地方做所有的事情,无论是在属性中,比如

spring.rabbitmq.listener.simple.default-requeue-rejected=false
spring.rabbitmq.listener.simple.concurrency=5
spring.rabbitmq.listener.simple.max-concurrency=10

或者完全在java中,比如

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setConcurrentConsumers(5);
        factory.setMaxConcurrentConsumers(10);
        factory.setDefaultRequeueRejected(false);
        return factory;
    }

上面这两个是一样的。

我希望当我使用第二个 (java) 选项时仍然从 application.properties 中选取属性,然后我在 java 中进行自定义,但它不会像这样工作。 是的,“复制粘贴”是邪恶的 :)