使用AMQP.Node在RabbitMQ和节点中不消耗死字母消息

时间:2013-07-31 11:32:11

标签: node.js rabbitmq dead-letter

我希望在我的一名工作人员一段时间后收到一条消息。在发现所谓的死信交换后,我决定使用Node和RabbitMQ。

消息似乎被发送到DeadExchange中的队列,但消费者永远不会在WorkExchange中的WorkQueue中经过的时间之后收到消息。 bindQueue是关闭的,还是死信不起作用?

我现在尝试了很多不同的 。有人可以指出我错过了什么吗?

var amqp = require('amqplib');
var url = 'amqp://dev.rabbitmq.com';

amqp.connect(url).then(function(conn) {
    //Subscribe to the WorkQueue in WorkExchange to which the "delayed" messages get dead-letter'ed (is that a verb?) to.
    return conn.createChannel().then(function(ch) {
        return ch.assertExchange('WorkExchange', 'direct').then(function() {
            return ch.assertQueue('WorkQueue', {
                autoDelete: false,
                durable: true
            })
        }).then(function() {
            return ch.bindQueue('WorkQueue', 'WorkExchange', '');
        }).then(function() {
            console.log('Waiting for consume.');

            return ch.consume('WorkQueue', function(msg) {
                console.log('Received message.');
                console.log(msg.content.toString());
                ch.ack(msg);
            });
        });
    })
}).then(function() {
    //Now send a test message to DeadExchange to a random (unique) queue.
    return amqp.connect(url).then(function(conn) {
        return conn.createChannel();
    }).then(function(ch) {
        return ch.assertExchange('DeadExchange', 'direct').then(function() {
            return ch.assertQueue('', {
                arguments: {
                    'x-dead-letter-exchange': 'WorkExchange',
                    'x-message-ttl': 2000,
                    'x-expires': 10000
                }
            })
        }).then(function(ok) {
            console.log('Sending delayed message');

            return ch.sendToQueue(ok.queue, new Buffer(':)'));
        });
    })
}).then(null, function(error) {
    console.log('error\'ed')
    console.log(error);
    console.log(error.stack);
});

我正在使用amq中的amqplib amqp.node(https://github.com/squaremo/amqp.node)。虽然node-amqp(https://github.com/postwait/node-amqp)似乎更受欢迎,但它没有实现完整的协议,并且在重新连接方面存在一些悬而未决的问题。

dev.rabbitmq.com正在运行RabbitMQ 3.1.3。

3 个答案:

答案 0 :(得分:3)

这是一个有效的代码。当一条消息在DeadExchange中花费超过ttl时,它会被推送到WorkExchange。成功的关键是定义正确的路由密钥。您希望发送到ttl的交换队列应该使用路由密钥(注意:不是默认值)和' x-dead-letter-routing-key'属性值应该与该路由键匹配。



var amqp = require('amqplib');
var url = 'amqp://localhost';

amqp.connect(url).then(function(conn) {
    //Subscribe to the WorkQueue in WorkExchange to which the "delayed" messages get dead-letter'ed (is that a verb?) to.
    return conn.createChannel().then(function(ch) {
        return ch.assertExchange('WorkExchange', 'direct').then(function() {
            return ch.assertQueue('WorkQueue', {
                autoDelete: false,
                durable: true
            })
        }).then(function() {
            return ch.bindQueue('WorkQueue', 'WorkExchange', 'rk1');
        }).then(function() {
            console.log('Waiting for consume.');

            return ch.consume('WorkQueue', function(msg) {
                console.log('Received message.');
                console.log(msg.content.toString());
                ch.ack(msg);
            });
        });
    })
}).then(function() {
    //Now send a test message to DeadExchange to DEQ queue.
    return amqp.connect(url).then(function(conn) {
        return conn.createChannel();
    }).then(function(ch) {
        return ch.assertExchange('DeadExchange', 'direct').then(function() {
            return ch.assertQueue('DEQ', {
                arguments: {
                    'x-dead-letter-exchange': 'WorkExchange',
                    'x-dead-letter-routing-key': 'rk1',
                    'x-message-ttl': 15000,
                    'x-expires': 100000
                }
            })
        }).then(function() {
            return ch.bindQueue('DEQ', 'DeadExchange', '');
        }).then(function() {
            console.log('Sending delayed message');

            return ch.publish('DeadExchange', '', new Buffer("Over the Hills and Far Away!"));
        });
    })
}).then(null, function(error) {
    console.log('error\'ed')
    console.log(error);
    console.log(error.stack);
});




答案 1 :(得分:0)

AMQP.Node中的Channel#assertQueue中有一个错误,它已修复,请参阅https://github.com/squaremo/amqp.node/commit/3749c66b448875d2df374e6a89946c0bdd0cb918。修复程序在GitHub上,但尚未在npm中。

答案 2 :(得分:0)

这是一个使用AMQP Connection Manager for Node的示例。我注意到似乎没有任何示例与我们在代码中所做的匹配,因此我用一个简单的示例创建了一个回购协议,并通过重新发布回主交换器来进行重试计数:https://github.com/PritchardAlexander/node-amqp-dead-letter-queue

这是简单的例子:

const amqp = require('amqp-connection-manager');
const username = encodeURIComponent('queue');
const password = encodeURIComponent('pass');
const port = '5672';
const host = 'localhost';
const connectionString = `amqp://${username}:${password}@${host}:${port}`;

// Ask the connection manager for a ChannelWrapper.  Specify a setup function to
// run every time we reconnect to the broker.
connection = amqp.connect([connectionString]);

// A channel is your ongoing connection to RabbitMQ.
// All commands go through your channel.
connection.createChannel({
  json: true,
  setup: function (channel) {
    channel.prefetch(100);

    // Setup EXCHANGES - which are hubs you PUBLISH to that dispatch MESSAGES to QUEUES
    return Promise.all([
      channel.assertExchange('Test_MainExchange', 'topic', {
        durable: false,
        autoDelete: true,
        noAck: false
      }),
      channel.assertExchange('Test_DeadLetterExchange', 'topic', {
        durable: false,
        autoDelete: true,
        maxLength: 1000,
        noAck: true // This means dead letter messages will not need an explicit acknowledgement or rejection
      })
    ])
    // Setup QUEUES - which are delegated MESSAGES by EXCHANGES.
    // The MESSAGES then need to be CONSUMED.
    .then(() => {
      return Promise.all([
        channel.assertQueue(
          'Test_MainQueue',
          options = {
            durable: true,
            autoDelete: true,
            exclusive: false,
            messageTtl: 1000*60*60*1,
            deadLetterExchange: 'Test_DeadLetterExchange'
          }
        ),
        channel.assertQueue('Test_DeadLetterQueue',
          options = {
            durable: false,
            autoDelete: true,
            exclusive: false
          }
        )
      ]);
    })
    // This glues the QUEUES and EXCHANGES together
    // The last parameter is a routing key. A hash/pound just means: give me all messages in the exchange.
    .then(() => {
      return Promise.all([
        channel.bindQueue('Test_MainQueue', 'Test_MainExchange', '#'),
        channel.bindQueue('Test_DeadLetterQueue', 'Test_DeadLetterExchange', '#')
      ]);
    })
    // Setup our CONSUMERS
    // They pick MESSAGES off of QUEUES and do something with them (either ack or nack them)
    .then(() => {
      return Promise.all([
        channel.consume('Test_MainQueue', (msg) => {
          const stringifiedContent = msg.content ? msg.content.toString() : '{}';
          console.log('Test_MainQueue::CONSUME ' + stringifiedContent);

          const messageData = JSON.parse(stringifiedContent);
          if (messageData.value === 0) {
            console.log('Test_MainQueue::REJECT ' + stringifiedContent);
            // the 'false' param at the very end means, don't retry! dead letter this instead!
            return channel.nack(msg, true, false);
          }
          return channel.ack(msg);
        })
      ]),
      channel.consume('Test_DeadLetterQueue', (msg) => {
        const stringifiedContent = msg.content ? msg.content.toString() : '{}';
        console.log('');
        console.log('Test_DeadLetterQueue::CONSUME ' + stringifiedContent);
        console.log('');
      });
    })
    .then(() => {
      setInterval(function () {
        const messageData = {
          text: 'Dead letter if 0',
          value: Math.floor(Math.random()*5)
        };
        const stringifiedMessage = JSON.stringify(messageData);

        // Publish message to exchange
        if (channel.publish('Test_MainExchange', '', new Buffer(stringifiedMessage))) {
          console.log(`Sent ${stringifiedMessage}`);
        } else {
          console.log(`Failed to send ${stringifiedMessage}`);
        };
      }, 300);
    });
  }
});