带有rhea的Node.js中的AMQP 1.0临时队列

时间:2018-10-18 14:06:40

标签: node.js rabbitmq activemq rpc amqp

我有一段时间在使用RabbitMQ。我有几种微服务使用基本的RPC机制运行,非常类似于Rabbit tutorials中描述的那种。我试图使用rhea切换到AMQP 1.0,因为我需要使用Amazon Active MQ。但是我仍然停留在复制此简单模式上:

ch.assertQueue('', {exclusive: true}, function(err, q) {
 let corr = //some UUID
   ch.consume(q.queue, function(msg) {
     /* */
   });

ch.sendToQueue('rpc_queue',
      "TEST2",
      { correlationId: corr, replyTo: q.queue });
    });
})

我不能从rhea得到的是有可能拥有临时队列(与客户端连接有关),然后将“ replyTo”添加到这些队列。

我尝试过:

client.open_receiver({
    source: { address: "rpc:callback", expiry_policy: "connection-close" }
  });

使用expiry_policy,但是不起作用(我甚至尝试使用AMQP 1.0插件和Apache Active MQ来使用RabbitMQ)。

重点是:

  1. 我希望有一个临时(专有)队列,该队列在客户端连接断开时会自动断开
  2. 使用该临时队列(我可以手动为其指定临时名称,这不是重点)来解决ReplyTo消息

,但我既无法获取临时队列(在AMQP 0.9.1中不包含),也无法使用该名称来处理ReplyTo消息。 我想念什么?

3 个答案:

答案 0 :(得分:0)

这是Rhea源代码树中的请求-响应示例:

https://github.com/amqp/rhea/blob/master/examples/client.js

创建临时队列的部分在此处(请注意有关“动态”的部分-即服务器生成的源地址):

https://github.com/amqp/rhea/blob/master/examples/client.js#L40

在此处根据请求设置答复队列:

https://github.com/amqp/rhea/blob/master/examples/client.js#L34

它用于以下服务器上的回复处理:

https://github.com/amqp/rhea/blob/master/examples/server.js#L26

这是另一个示例。它使用相同的请求-响应机制:

https://github.com/ssorj/messaging-examples/blob/master/rhea/request.js https://github.com/ssorj/messaging-examples/blob/master/rhea/respond.js

我知道ActiveMQ可与AMQP 1.0动态源一起使用,但是我不确定RabbitMQ。我建议您使用ActiveMQ进行测试,因为最终您要使用它。

答案 1 :(得分:0)

const container = require("rhea");
const _logger = require("pino")();
const nanoid = require("nanoid");

const init = ({ config, caller, resources, services, rpcs }) => {
  return new Promise((resolve, reject) => {
    let _rpcs = {};
    let _responses = {};

    const send = (sender, receiver, correlation_id, body) => {
      if (receiver.source.address) {
        sender.send({
          reply_to: receiver.source.address,
          correlation_id,
          body
        });
      }
    };

    container.on("connection_open", context => {
      //RPCS
      rpcs &&
        rpcs.forEach(sendTo => {
          let parts = sendTo.name.split(".");
          _rpcs[parts[0]] = _rpcs[parts[0]] ? _rpcs[parts[0]] : {};

          let sender = context.connection.open_sender(sendTo.name);
          let receiver = context.connection.open_receiver({
            source: { dynamic: true }
          });

          receiver.on("message", context => {
            let correlation_id = context.message.correlation_id;
            if (_responses[correlation_id]) {
              let { resolve, reject } = _responses[correlation_id];
              resolve(context.message.body);
              delete _responses[correlation_id];
            }
          });

          _rpcs[parts[0]][parts[1]] = body =>
            new Promise((resolve, reject) => {
              const correlation_id = nanoid();
              _responses[correlation_id] = { resolve, reject };
              send(sender, receiver, correlation_id, body);
            });
        });

      // SERVICES
      services &&
        services.forEach(service => {
          let receiver = context.connection.open_receiver({
            source: `${resources.name}.${service.name}`,
            //credit_window: 1, //service.prefetch || 500,
            autoaccept: false
          });

          receiver.on("message", async context => {
            let request = context.message;
            let reply_to = request.reply_to;
            let payload = request.body;

            try {
              let response = {
                to: reply_to,
                body: await caller(service.responder)({ payload })
              };
              if (request.correlation_id) {
                response.correlation_id = request.correlation_id;
              }
              context.connection.send(response);
              context.delivery.accept();
            } catch (error) {
              _logger.error(error);
              context.delivery.reject();
            }
          });
        });
    });

    container.on("receiver_open", context => {
      resolve(_rpcs);
    });

    container.on("connection_error", error => _logger.error(error));

    container.connect(config.getResource("amqp"));
  });
};

module.exports = { init };

答案 2 :(得分:0)

本质上归结为这个流程。从下往上阅读,因为这是事物的逻辑流程。您首先启动您的侦听器,该侦听器将创建具有随机名称的动态队列。然后,您打开您的发件人以及来自传入的 context 的回复队列的名称。

// 'conn' is the rhea connection you have already created

new Promise((resolve, reject) => {
    let replyToQueue;

    conn.on('message', (context) => {
        // you have received your message
        resolve(context.message);
    });

    conn.once('sendable', (context) => {
        // send a message with a reply_to header
        context.sender.send({
            reply_to: replyToQueue,
            body: 'some message content'
        });
    });

    conn.on('receiver_open', (context) => {
        // capture the name of that dynamically named queue here
        replyToQueue = context.receiver.source.address;
        conn.open_sender('queue://send.to');
    });

    // start listening to a dynamically named temporary queue
    conn.open_receiver({ source: { dynamic: true } });
}