使用RabbitMQ时为每个远程方法创建队列?

时间:2015-07-28 21:43:31

标签: rabbitmq rpc mq

让我们暂时接受实施RPC over消息队列(如RabbitMQ)并不是一个可怕的想法 - 有时在与旧系统接口时可能是必要的。

如果是RPC over RabbitMQ,客户端会向代理发送消息,代理会将消息路由到工作人员,工作人员会通过代理将结果返回给客户端。但是,如果一个worker实现了多个远程方法,那么不同的调用需要路由到不同的侦听器。

这种情况下的一般做法是什么?所有RPC over MQ示例仅显示一个远程方法。将方法名称设置为路由规则/队列名称会很好,也很容易,但我不知道这是否是正确的方法。

3 个答案:

答案 0 :(得分:13)

  

让我们暂时接受一下,实现RPC over消息队列(如RabbitMQ)并不是一个可怕的想法

它根本不可怕!它很常见,并且在许多情况下都被推荐 - 而不仅仅是传统集成。

...好的,现在问你的实际问题:)

从非常高的角度来看,这是你需要做的。

您的请求和回复需要有两个关键信息:

  • a correlation-id
  • 一个reply-to队列

这些信息将允许您关联原始请求和响应。

发送请求之前

让您的请求代码为自己创建一个独占队列。此队列将用于接收回复。

创建新的相关ID - 通常是GUID或UUID以保证唯一性。

发送请求时

将您生成的相关ID附加到邮件属性。您应该使用correlationId属性。

将相关ID与请求的关联回调函数(回复处理程序)一起存储,该请求位于发出请求的代码内部。当回复进来时你需要这个。

将您创建的独占队列的名称附加到邮件的replyTo属性中。

完成所有这些后,您可以通过rabbitmq发送消息

回复时

回复代码需要同时使用原始邮件中的correlationIdreplyTo字段。所以一定要抓住那些

应将答复直接发送到replyTo队列。不通过交易所使用标准出版。相反,使用"发送到队列"将回复消息直接发送到队列。您正在使用的任何库的功能,并将响应直接发送到replyTo队列。

请务必在回复中包含correlationId。这是回答你问题的关键部分

处理回复时

发出原始请求的代码将从replyTo队列接收消息。然后它会将correlationId拉出消息属性。

使用关联id来查找请求的回调方法...处理响应的代码。将消息传递给此回调方法,您已经完成了很多工作。

实施细节

这从高层次的角度来看是有效的。当您深入了解代码时,实现细节将根据您使用的语言和驱动程序/库而有所不同。

任何给定语言的大多数优秀RabbitMQ库都会内置请求/响应。如果你没有,你可能想要寻找一个不同的库。除非您在AMQP协议之上编写基于模式的库,否则您应该寻找一个为您实现通用模式的库。

如果您需要有关请求/回复模式的更多信息,包括我在此处提供的所有详细信息(以及更多信息),请查看以下资源:

如果您在Node.js中工作,我建议您使用wascally库,其中包含您需要的请求/回复功能。对于Ruby,请查看bunny。对于Java或.NET,请查看一些服务总线实现。在.NET中,我推荐使用NServiceBus或MassTransit。

答案 1 :(得分:3)

我发现每个请求使用一个新的回复队列可能会非常低效,特别是在群集上运行RabbitMQ时。

正如评论direct reply-to中所建议的那样,似乎是要走的路。我已经记录了here我在解决之前尝试过的所有选项。

答案 2 :(得分:0)

我写了一个npm软件包amq.rabbitmq.reply-to.js

  • 使用直接答复-一种功能,该功能允许RPC(请求/答复)客户端的设计与教程6(https://www.rabbitmq.com/direct-reply-to.html)中所示的设计类似,从而避免为每个请求声明响应队列。 / p>

  • 创建一个事件发射器,其中rpc响应将通过correlationId发布 根据{{​​3}}

  • 的建议

用法:

const rabbitmqreplyto = require('amq.rabbitmq.reply-to.js');

const serverCallbackTimesTen = (message, rpcServer) => {
    const n = parseInt(message);
    return Promise.resolve(`${n * 10}`);
};

let rpcServer;
let rpcClient;
Promise.resolve().then(() => {
    const serverOptions = new rabbitmqreplyto.RpcServerOptions(
    /* url */ undefined, 
    /* serverId */ undefined, 
    /* callback */ serverCallbackTimesTen);

    return rabbitmqreplyto.RpcServer.Create(serverOptions);
}).then((rpcServerP) => {
    rpcServer = rpcServerP;
    return rabbitmqreplyto.RpcClient.Create();
}).then((rpcClientP) => {
    rpcClient = rpcClientP;
    const promises = [];
    for (let i = 1; i <= 20; i++) {
        promises.push(rpcClient.sendRPCMessage(`${i}`));
    }
    return Promise.all(promises);
}).then((replies) => {
    console.log(replies);
    return Promise.all([rpcServer.Close(), rpcClient.Close()]);
});

//['10',
//  '20',
//  '30',
//  '40',
//  '50',
//  '60',
//  '70',
//  '80',
//  '90',
//  '100',
//  '110',
//  '120',
//  '130',
//  '140',
//  '150',
//  '160',
//  '170',
//  '180',
//  '190',
//  '200']