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

使用RabbitMQ时是否为每个远程方法创建队列?,rabbitmq,rpc,mq,Rabbitmq,Rpc,Mq,让我们暂时接受一下,在消息队列上实现RPC(比如RabbitMQ)并不是一个可怕的想法——有时在与遗留系统接口时可能是必要的 在RPC over RabbitMQ的情况下,客户端向代理发送消息,代理将消息路由到工作进程,工作进程通过代理将结果返回给客户端。但是,如果一个工作者实现了多个远程方法,那么不同的调用需要路由到不同的侦听器 这种情况下的一般做法是什么?所有RPC over MQ示例仅显示一个远程方法。只需将方法名设置为路由规则/队列名即可,但我不知道这是否是正确的方法 让我们暂时接受在消

让我们暂时接受一下,在消息队列上实现RPC(比如RabbitMQ)并不是一个可怕的想法——有时在与遗留系统接口时可能是必要的

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

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

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

一点也不可怕!它很常见,并且在许多情况下都建议使用,而不仅仅是旧式集成

。。。好,现在回答你的实际问题:)


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

您的请求和响应需要有两条关键信息:

  • a
    相关id
  • 答复队列
这些信息将允许您关联原始请求和响应

在发送请求之前 让您的请求代码为自己创建一个独占队列。此队列将用于接收答复

创建新的关联id—通常是GUID或UUID,以保证唯一性

发送请求时 将生成的相关id附加到消息属性。您应该为此使用
correlationId
属性

将相关id与请求的相关回调函数(应答处理程序)一起存储在发出请求的代码中的某个位置。当收到答复时,您需要对此进行修改

将您创建的独占队列的名称也附加到消息的
replyTo
属性

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

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

回复应直接发送到
replyTo
队列。不要通过exchange使用标准发布。相反,使用您正在使用的任何库的“发送到队列”功能将回复消息直接发送到队列,并将响应直接发送到
replyTo
队列

还要确保在响应中包含
correlationId
。这是回答你问题的关键部分

在处理答复时, 发出原始请求的代码将从
replyTo
队列接收消息。然后,它将从消息属性中拉出
correlationId

使用相关id查找请求的回调方法。。。处理响应的代码。将消息传递给这个回调方法,您就完成了

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

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

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

  • 我自己的电子邮件课程/电子书
  • -务必购买完整描述/实现模式的书籍。这本书值得拥有

如果您使用的是Node.js,我建议您使用这个库,它包含您需要的请求/回复功能。对于Ruby,请查看。对于Java或.NET,请查看周围许多服务总线实现中的一些。在.NET中,我建议使用NServiceBus或MassTransit

我发现,对每个请求使用新的应答队列会变得非常低效,特别是在集群上运行RabbitMQ时

正如评论中所建议的那样,这似乎是一条路要走。我已经记录了我在解决这个问题之前尝试过的所有选项。

我编写了一个npm包,其中包括:

  • 使用直接回复-此功能允许设计类似于教程6()的RPC(请求/回复)客户端避免为每个请求声明响应队列

  • 创建事件发射器,其中rpc响应将由correlationId发布 按照

用法:
const rabbitmqreplyto=require('amq.rabbitmq.reply to.js');
const serverCallbackTimesTen=(消息,rpcServer)=>{
const n=parseInt(消息);
返回承诺。解析(`n*10}`);
};
让rpcServer;
让RPC客户;
Promise.resolve()然后(()=>{
const serverOptions=new rabbitmqreplyto.RpcServerOptions(
/*url*/未定义,
/*serverId*/未定义,
/*回调*/serverCallbackTimesTen);
返回rabbitmqreplyto.RpcServer.Create(serverOptions);
})。然后((rpcServerP)=>{
rpcServer=rpcServerP;
返回rabbitmqreplyto.RpcClient.Create();
})。然后((rpcClientP)=>{
rpcClient=rpcClientP;
常量承诺=[];
for(设i=1;i{
控制台日志(回复);
返回Promise.all([rpcServer.Close(),rpcClient.Close()]);
});
//['10',
//  '20',
//  '30',
//  '40',
//  '50',
//  '60',
//  '70',
//  '80',
//  '90',
//  '100',
//  '110',
//  '120',
//  '130',
//  '140',
//  '150',
//
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']