简体   繁体   中英

basic.Nack not being processed

Here is what I am trying to do:

  1. Dequeue a message
  2. Do an action with the message
  3. If the action fails, put the message back in the queue
  4. If the action succeeds, acknowledge the message

My problem right now is that, if the action fails, the message isn't re-queued, but stays unacknowledged. If I go in RabbitMQ web configuration interface, I see that the messages are flagged as unacknowledged, even though the basic.Nack has been stepped over.

var delivery = subscription.Next();

var messageBody = delivery.Body;

try
{
   action.Invoke(messageBody);
   subscription.Ack(delivery);
}
catch (Exception ex)
{
   subscription.Model.BasicNack(delivery.DeliveryTag, false, true);
   throw ex;
}

Update:

So I've noticed that Messages go from Ready to Unacknowledged really fast. A rate way faster then I'm actually calling subscriber.Next(), as if the the .Net client caches all the messages in memory (the memory foot print of my app is actually growing quite fast), and processes those messages from memory and sends the Ack() afterwards, unflagging the message from Unacknowledged.

Update 2:

Seems like the queue being emptied really fast was because I hadn't set BasicQos on my Model. The following fixed everything. Basic.Nack() still doesn't seem to work tho:

Model.BasicQos(0, 1, false)

I suspect you're using:
channel.BasicConsume(your_queue_name, false, consumer); to retrieve messages.

I ran several tests with a RabbitMQ 3.2.4 server and client. I was unable to get either channel.BasickAck(...) or channel.BasicNack(...) to work as expected.

That said, I was able to get the expected Ack | Nack behavior when I used:
BasicGetResult result = channel.BasicGet(your_queue_name, false);

So you may want to consider a different retrieval method to get messages. I realize that the Consume & Dequeue are the "preferred" methods but they weren't working in my case. I wanted fair, one-at-a-time dispatch with acknowledgments. Using BasicGet was the only way I could achieve that.

The downside to that approach is you'll possibly lose the client side event iterator you're using with subscription.Next() .


If I had to venture a guess, I think that something about the local Queue collection is messing up the channel's ability to provide an acknowledgement. And it's worth pointing out that creating the consumer with new QueueingBasicConsumer(channel); triggers a call to pre-fetch events from the server's queue. The consumer's Queue is just a SharedQueue<RabbitMQ.Client.Events.BasicDeliverEventArgs> and SharedQueue is just an extension of IEnumerable.

Also keep in mind that the same channel that pulls the message needs to provide the Ack | Nack. You cannot Ack | Nack a message from a different channel . Or at least I haven't figured out how to do so, nor have others. That's a problem if you wrap your RabbitMQ objects within using statements (so you don't leave network resources laying around) and you have long process to run before you can safely acknowledge.

This SO Answer lays out a decent workflow to get around the likely reality that your pulling channel is not going to be the channel that sends the Ack | Nack. The trick is setting a TTL and not bothering with sending a Nack - just let the new message expire and requeue automatically.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM