如何通过.NET RabbitMQ客户端为用户使用预取计数

时间:2019-02-08 08:18:28

标签: c# rabbitmq messaging

它在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进行手动确认?是否安全

2 个答案:

答案 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)不是。

RabbitMQ and channels Java thread safety

中也提到了一些要点