它在RabbitMQ文档中声明了以下内容
”根据经验,在线程之间共享Channel实例是 要避免的事情。应用程序应优先使用频道 每个线程,而不是在多个线程之间共享同一频道 线程。”
当前,我们正在研究预取计数,建议如果您的使用者数量少且autoack = false,则应该一次使用很多消息。但是,我们发现,如果使用者使用单个执行线程将手动确认发送回去,则预取将不起作用。但是,如果将使用者处理包装在Task中,则会发现预取计数确实很重要,并且可以大大提高使用者的性能。
请参见以下示例,其中我们将使用者的消息消费包装在Task对象中:
class Program
{
public static void Main()
{
var factory = new ConnectionFactory()
{
HostName = "172.20.20.13",
UserName = "billy",
Password = "guest",
Port = 5671,
VirtualHost = "/",
Ssl = new SslOption
{
Enabled = true,
ServerName = "rabbit.blah.com",
Version = System.Security.Authentication.SslProtocols.Tls12
}
};
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
channel.BasicQos(0, 100, false);
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var queueName = channel.QueueDeclare().QueueName;
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var _result = new Task(() => {
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
System.Threading.Thread.Sleep(80);
channel.BasicAck(ea.DeliveryTag, false);
});
_result.Start();
};
channel.BasicConsume(queue: "test.queue.1", autoAck: false, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
我要问的问题是,人们如何利用预取计数的.NET Rabbitmq客户端实现消费者?您是否必须使用某种Task进行手动确认?是否安全
答案 0 :(得分:2)
您参考的文档适用于Java客户端。您应该改为指this document。
您使用的是最新版本的.NET客户端(5.1
),因此在Received
事件处理程序中进行操作不会阻止其他处理TCP数据的线程,因此它将不会阻止心跳-两者都很好。
首先,调用channel.BasicQos(0, 1, false)
意味着您的消费者一次只能从RabbitMQ收到一条准备好的消息,直到调用BasicAck
为止,将不会再发送另一条消息。因此,实际上根本没有理由在另一个线程中进行工作,因为无论如何您都不会收到其他消息。
如果您增加预取值(通过实验和运行基准测试),并且要花费几毫秒以上的时间,则必须在后台线程中进行工作。
在Received
事件回调中进行工作时,由于该回调未在其自己的线程上执行,因此它将阻塞用于进行该回调的线程。因此,您可以确保工作很短,也可以在另一个线程中进行工作。
我刚刚花了一些时间查看.NET客户端代码,并且我很确定IModel
实例不是线程安全的。如果增加预取,您将有机会同时确认多条消息,因此我建议实现一个使用该消息的解决方案,并确保在创建连接的同一线程上调用BasicAck
。
注意: RabbitMQ团队监视rabbitmq-users
mailing list,并且有时仅在StackOverflow上回答问题。
答案 1 :(得分:1)
来源:https://www.rabbitmq.com/api-guide.html
使用手动确认时,重要的是要考虑 确认使用哪个线程。如果与 收到交货的线程(例如Consumer#handleDelivery 将交付处理委派给其他线程),并通过 将multiple参数设置为true是不安全的,将导致 双重确认,因此是通道级协议 关闭通道的异常。在确认一条消息 时间可以安全。
channel.basicAck(tag, false)
是线程安全的
但是consumerChannel.basicAck(tag, true)
不是。