.NET AMQP消息传递模式问题

时间:2014-09-24 13:06:10

标签: c# events rabbitmq messaging eai

我使用RabbitMQ创建了一个小类,它在主题交换上实现了发布/订阅消息传递模式。在这个pub / sub之上我有方法和属性:

  1. void发送(消息,主题) - 将消息发布到目标主题,供任何订阅者处理。

  2. MessageReceivedEvent - 订阅此消息传递实例上的消息接收事件(消息传递实例在创建时绑定到所需的订阅主题)。

  3. SendWaitReply(消息,主题) - 发送消息并阻止,直到收到回复消息,其相关ID与发送的消息ID(或超时)相匹配。这实际上是在pub / sub模式之上的请求/回复或RPC机制。

  4. 由于系统的设计方式,我所选择的消息传递模式有点不尽相同。我意识到我可以使用回复队列来缓解SendWaitReply的潜在问题,但这会破坏一些要求。

    现在我的问题是:

    • 对于Listen事件,当侦听器在单个线程中运行时,将通过事件订阅者同步处理消息。在处理大量消息时(即在后端进程中使用来自web api的事件),这会导致一些严重的性能问题。我正在考虑传递一个回调函数,而不是订阅一个事件,然后使用Task或Threadpool并行调度回调集合。线程安全显然现在是调用者关注的问题。我不确定这是否是正确的做法。

    • 对于SendWaitReply事件,我构建了一个似乎是一个hacky解决方案,它接收来自消息侦听器循环的所有入站消息,如果它们包含非空关联guid,则将它们放在ConcurrentDictionary中。然后在SendWaitReply方法中,我在ConcurrentDictionary上查询一条消息,该消息包含与发送消息的Id匹配的密钥(或在一段时间后超时)。如果有更快/更好的方法来做到这一点,我真的想调查一下。也许是一种向所有当前被阻止的SendWaitReply方法发出信号的方式,即新消息可用,他们应该检查他们的ID而不是连续轮询?


    2014年10月15日更新

    经过多次详尽的研究后,我得出的结论是,没有"官方"机制/帮助器/库直接处理上面针对RabbitMQ或AMQP范围内的 SendWaitReply 提出的特定用例。我将坚持目前的解决方案(并研究更强大的实现)。有一些答案建议我使用提供的RPC功能,但不幸的是,这只适用于您希望在每个请求的基础上使用独占回调队列的情况。这打破了我在同一主题交换中显示所有消息(请求和回复)的主要要求之一。

    为进一步澄清, SendWaitReply 请求的典型消息对采用以下格式:

    • Topic_Exchange.Service_A => some_command =>的 Topic_Exchange.Service_B
    • Topic_Exchange.Service_B => some_command_reply =>的 Topic_Exchange.Service_A

    这为我提供了一个强大的调试和日志记录技术,我只需在 Topic_Exchange。#上设置一个监听器,就可以看到跟踪非常深的呼叫堆栈的所有系统流量&#39 ;通过各种服务。

    TL; DR - 当前问题

    从架构层面退缩 - 我仍然遇到消息监听器循环的问题。我已经尝试了 EventingBasicConsumer 并且仍然看到了一个块。我的类的工作方式是调用者订阅由类的实例提供的委托。消息循环触发该委托上的事件,然后这些订阅者处理该消息。似乎我需要一种不同的方式将消息事件处理程序传递给实例,这样他们就不会坐在一个执行同步处理的委托后面。

1 个答案:

答案 0 :(得分:1)

很难说为什么你的代码在没有样本的情况下阻塞,但为了防止在使用时阻塞,你应该使用EventingBasicConsumer。

var consumer = new EventingBasicConsumer;
consumer.Received += (s, delivery) => { /* do stuff here */ };
channel.BasicConsume(queue, false, consumer);

有一点需要注意,如果您使用autoAck = false(就像我一样),那么您需要确保在执行channel.BasicAck时锁定通道,否则您可能会遇到.NET库中的并发问题。

对于SendWaitReply,如果你只使用RabbitMQ客户端库中包含的SimpleRpcClient,你可能会有更好的运气:

var props = channel.CreateBasicProperties();
// Set your properties
var client = new RabbitMQ.Client.MessagePatterns.SimpleRpcClient(channel, exchange, ExchangeType.Direct, routingKey);
IBasicProperties replyProps;
byte[] response = client.Call(props, body, out replyProps);

SimpleRpcClient将处理创建临时队列,关联ID等,而不是构建自己的队列。如果您发现想要做更高级的事情,source也是一个很好的参考。