C# RabbitMQ中的并发性
经过一周的编码和搜索论坛,现在似乎是时候问 我有一个C#应用程序,它使用EventingBasicConsumer处理RabbitMQ发送的消息。我想同时处理多条消息,因此我在同一个连接上实例化了几个通道(本例中为8个),每个通道都有一个使用者。然后,我将事件处理程序附加到每个使用者收到的事件。根据我到目前为止的所有阅读资料,此设置应允许事件处理程序由使用者同时触发,每个使用者在其自己的线程中运行。但在我的例子中,消费者只有在前一个消费者确认其消息后才能按顺序接收消息 还有其他人经历过这种行为吗?我的理解是否正确,在这种情况下,处理在技术上应该是并行的 下面是更好地说明此问题的基本代码:C# RabbitMQ中的并发性,c#,multithreading,concurrency,rabbitmq,C#,Multithreading,Concurrency,Rabbitmq,经过一周的编码和搜索论坛,现在似乎是时候问 我有一个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)自定义控制并发级别。