以下是我要做的事情:
我现在的问题是,如果操作失败,则邮件不会重新排队,但不会被确认。如果我进入RabbitMQ Web配置界面,我会看到消息被标记为未确认,即使basic.Nack已被切换。
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;
}
更新
所以我注意到消息从Ready到Unacknowledged非常快。速率方式比我实际调用subscriber.Next()快,好像.Net客户端缓存内存中的所有消息(我的应用程序的内存占用量实际上增长得非常快),并从内存中处理这些消息然后发送Ack(),解除Unacknowledged的消息。
更新2:
似乎队列被快速清空是因为我没有在我的模型上设置BasicQos。以下修正了一切。 Basic.Nack()似乎仍然没有工作:
Model.BasicQos(0,1,false)
答案 0 :(得分:1)
我怀疑你正在使用:
channel.BasicConsume(your_queue_name, false, consumer);
检索邮件。
我用RabbitMQ 3.2.4服务器和客户端运行了几次测试。我无法让channel.BasickAck(...)
或channel.BasicNack(...)
按预期工作。
那就是说,我能够获得预期的Ack |我使用时的Nack行为:
BasicGetResult result = channel.BasicGet(your_queue_name, false);
因此,您可能需要考虑使用不同的检索方法来获取消息。我意识到消费与消费者出列人员是首选的"方法,但他们不是在我的情况下工作。我希望公平,一次一次地发送确认函。使用BasicGet是我实现这一目标的唯一方法。
该方法的缺点是您可能会丢失与subscription.Next()
一起使用的客户端事件迭代器。
如果我不得不猜测,我认为关于本地Queue集合的一些事情会破坏频道提供确认的能力。并且值得指出的是,使用new QueueingBasicConsumer(channel);
创建消费者会触发从服务器队列中预取事件的调用。消费者的队列只是SharedQueue<RabbitMQ.Client.Events.BasicDeliverEventArgs>
,而SharedQueue只是IEnumerable的扩展。
另请注意,拉取消息的相同频道需要提供Ack | NACK。 你不能Ack | Nack来自不同频道的消息。或者至少我还没有想出如何这样做,也没有其他人。如果你将RabbitMQ对象包含在using语句中(这样你就不会留下网络资源) 和 你需要很长时间才能运行这个问题在你能安全地承认之前。
这个SO Answer提供了一个不错的工作流程来解决你的拉动通道不将成为发送Ack的通道的可能现实。 NACK。诀窍是设置一个TTL,而不是在发送Nack时烦恼 - 只需让新消息过期并自动重新排队。