C# RabbitMQ中的并发性

C# RabbitMQ中的并发性,c#,multithreading,concurrency,rabbitmq,C#,Multithreading,Concurrency,Rabbitmq,经过一周的编码和搜索论坛,现在似乎是时候问 我有一个C#应用程序,它使用EventingBasicConsumer处理RabbitMQ发送的消息。我想同时处理多条消息,因此我在同一个连接上实例化了几个通道(本例中为8个),每个通道都有一个使用者。然后,我将事件处理程序附加到每个使用者收到的事件。根据我到目前为止的所有阅读资料,此设置应允许事件处理程序由使用者同时触发,每个使用者在其自己的线程中运行。但在我的例子中,消费者只有在前一个消费者确认其消息后才能按顺序接收消息 还有其他人经历过这种行为吗

经过一周的编码和搜索论坛,现在似乎是时候问

我有一个C#应用程序,它使用EventingBasicConsumer处理RabbitMQ发送的消息。我想同时处理多条消息,因此我在同一个连接上实例化了几个通道(本例中为8个),每个通道都有一个使用者。然后,我将事件处理程序附加到每个使用者收到的事件。根据我到目前为止的所有阅读资料,此设置应允许事件处理程序由使用者同时触发,每个使用者在其自己的线程中运行。但在我的例子中,消费者只有在前一个消费者确认其消息后才能按顺序接收消息

还有其他人经历过这种行为吗?我的理解是否正确,在这种情况下,处理在技术上应该是并行的

下面是更好地说明此问题的基本代码:

Initialise() {
    ConsumerChannels_ = new IModel[ConsumerCount_];
    Consumers_ = new EventingBasicConsumer[ConsumerCount_];
    for (int i = 0; i < ConsumerCount_; ++i)
    {
         ConsumerChannels_[i] = Connection_.CreateModel();
         Consumers_[i] = new EventingBasicConsumer(ConsumerChannels_[i]);
         Consumers_[i].Received += MessageReceived;
    }
}

MessageReceived(IBasicConsumer sender, BasicDeliverEventArgs e)
{
    int id = GetConsumerIndex(sender);
    Log_.Debug("Consumer " + id + ": processing started...");         
    // do some time consuming processing here
    sender.Model.BasicAck(e.DeliveryTag, false);
    Log_.Debug("Consumer " + id + ": processing ended.");
}
初始化(){
消费者通道=新IModel[消费者计数];
消费者=新事件基本消费者[消费者计数];
对于(int i=0;i
我希望看到的是类似于并行处理的东西

消费者1:已开始处理

消费者2:处理已开始

消费者3:处理已开始

消费者6:处理结束

消费者7:处理结束

消费者8:处理结束

但我得到的是顺序处理

消费者1:已开始处理

消费者1:处理结束

消费者2:处理已开始

消费者2:处理结束

消费者8:已开始处理

消费者8:处理结束


如果您有任何关于如何进行的想法,我们将不胜感激。

您必须找到以下方法:

通过在内部添加您自己的线程池来提高并发性:

MessageReceived(IBasicConsumer sender, BasicDeliverEventArgs e) {
    int id = GetConsumerIndex(sender);
    Log_.Debug("Consumer " + id + ": processing started...");         
    // do some time consuming processing here
    // PUT your thread-pool here and process the messages inside the thread

    sender.Model.BasicAck(e.DeliveryTag, false);
    Log_.Debug("Consumer " + id + ": processing ended."); }

}
注意:
BasicAck
可以在不同的线程中调用


您可以使用
QoS=1
向队列添加更多使用者,您可以循环使用消息您可以在创建
连接工厂时实际设置并行处理任务的数量

ConnectionFactory=newconnectionfactory
{
ConsumerDispatchConcurrency=2,
};
默认值为1,即串行/顺序处理

我通过解剖尸体发现了这一点。下面是有趣的部分(
concurrency
是从
ConsumerDispatchConcurrency
设置的):

Func loopStart=ProcessChannelAsync;
if(并发==1)
{
_worker=Task.Run(loopStart);
}
其他的
{
var tasks=新任务[并发];
for(int i=0;i
但要小心,这可能会导致比赛条件!该物业有以下备注:

对于大于1的并发性,这将取消消费者按照接收顺序处理消息的保证。除此之外,使用者还需要是线程/并发安全的


尽管有多个使用者,但它们都在同一线程上运行。您需要加速线程并为每个线程创建一个使用者。您还可以始终拥有一个使用者并多次运行处理应用程序。或者,在这里进行无耻的自我推销,使用类似于做重担的东西:)+1@Gabriele@起亚我们基本上是这样做的。它也简化了:就RabbitMQ而言,只有一个消费者。此外,无需手动启动线程/管理
ThreadPool
。将消息处理作为任务放在
任务调度器上
可以处理所有这些。通过改变
TaskScheduler
的最大并行度,可以轻松地在同一个使用者内上下扩展并行度(有几种类型的调度器提供这一功能)。专用线程池与共享默认的
ThreadPool
更广泛的讨论,这取决于您有多少这样的使用者以及需要的隔离程度。添加我自己的线程池成功了。知道我可以从自己的多个线程调用BasicAck是很有帮助的。对于任何感兴趣的人,我还介绍了一种简单的阻塞节流机制(使用Monitor.Wait和Monitor.Pulse),使用channel.BasicQos(0,10000,false)自定义控制并发级别。