簡體   English   中英

如何讓我的 RabbitMQ 消費者只收到一條消息而不會超時並再次發送?

[英]How can I have my RabbitMQ consumer receive just one message without it timing-out and sending agan?

我們有一個問題,如果一條消息處理時間過長,它會從未確認回到就緒狀態,然后再次發送。 有沒有辦法防止這種情況?

我們有一個這樣初始化的隊列消費者 class:

    private void Initialize()
    {
        string queueName = _configuration["QueueName"] + _configuration["QueueNameSuffixForSubscriber"];

        var factory = CreateConnectionFactory();

        var connection = factory.CreateConnection();
        _channel = connection.CreateModel();

        // The message TTL must match for QueueDeclare() to work.
        var arguments = new Dictionary<string, object>();
        arguments.Add("x-message-ttl", Convert.ToInt32(_configuration["EventBusMessageTtl"]));

        // By default, RabbitMQ dispatches all the messages to the first consumer. You can change this behaviour by
        // setting the BasicQos, this controls the no of messages a consumer can receive before it acknowledges it.
        _channel.BasicQos(0, 1, false);

        _channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: arguments);

        var consumer = new EventingBasicConsumer(_channel);
        consumer.Received += ConsumerReceived;

        _channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
    }

注意這一行:

_channel.BasicQos(0, 1, false);

這就是我們的消費者一次只提取一條消息的方式。 但是,如果該消息在發送 ack 之前花費的時間超過 2 分鍾,RMQ 將再次發送該消息。 我們不希望這樣。 (處理時間幾乎不會超過 2 分鍾,但我們不想滿足於差不多.)

有沒有辦法阻止 RMQ 再次發送消息? 我可以在處理消息之前發送 ack,但是我們會立即收到下一條消息,我們也不希望這樣。 我們希望在接受下一條消息之前等待消息完成處理。

如果我們可以在准備好時從 RMQ 中提取,那將解決它。

這是ConsumerReceived()方法:

    private void ConsumerReceived(object model, BasicDeliverEventArgs eventArgs)
    {
        try
        {
            var message = Encoding.UTF8.GetString(eventArgs.Body.ToArray());
            InvokeHandlers(eventArgs, message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error occurred in processing message or invoking handlers.");
        }
        finally
        {
            _channel.BasicAck(eventArgs.DeliveryTag, false);
        }
    }

我同意這似乎是輪詢而不是消費者訂閱的理想流程。 通常,您不想輪詢,因為它極大地損害了吞吐量,但在您的情況下,這正是您想要的。

while (true)
{
    BasicGetResult result = channel.BasicGet(queueName, noAck);
    if (result == null) 
    {
        // No message available at this time.
        // Sleep/delay to avoid cpu and I/O churn
        Thread.Sleep(2000);
    } 
    else 
    {
        try 
        {
            IBasicProperties props = result.BasicProperties;
            var message = Encoding.UTF8.GetString(result.Body.ToArray());
            InvokeHandlers(eventArgs, message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error occurred in processing message or invoking handlers.");
        }
        finally
        {
            _channel.BasicAck(eventArgs.DeliveryTag, false);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM