Manual message acknowledgments are turned on by default. In previous examples we explicitly turned them off by setting the autoAck ("automatic acknowledgement mode") parameter to true. It's time to remove this flag and manually send a proper acknowledgment from the worker, once we're done with a task.
默认情况下,手动的消息确认是打开的。在之前的例子里,我们通过设置“自动确认”为 true 值来显式的关闭了手动机制。现在是时候删除这个(自动)标记了,一旦我们的工作单元完成一个任务的时候,就手动地从工作单元发送一个恰当的确认。
var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine(" [x] Received {0}", message); int dots = message.Split('.').Length - 1; Thread.Sleep(dots * 1000); Console.WriteLine(" [x] Done"); channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); }; channel.BasicConsume(queue: "task_queue", autoAck: false, consumer: consumer);Using this code we can be sure that even if you kill a worker using CTRL+C while it was processing a message, nothing will be lost. Soon after the worker dies all unacknowledged messages will be redelivered.
使用这段代码后,我们可以确信即使你使用 CTRL + C 命令中止一个正在处理消息的工作单元也不会丢失什么。这样,在工作单元中止不久,所有未被确认的消息将会被重新递送。
Forgotten acknowledgment 被遗忘的确认It's a common mistake to miss the BasicAck. It's an easy error, but the consequences are serious. Messages will be redelivered when your client quits (which may look like random redelivery), but RabbitMQ will eat more and more memory as it won't be able to release any unacked messages.
一个较为常见的错误是忽视了 BasicAck,尽管这种错误很低级,但后果相当严重。当客户端退出时,消息会被重新递送(可能看起来像是随机地重新递送),但是 RabbitMQ 会因为无法释放那些未经确认的消息而吃掉越来越多的内存。
In order to debug this kind of mistake you can use rabbitmqctl to print the messages_unacknowledged field:
为了调试这种类型的错误,你可以使用 rabbitmqctl 命令来打印 messages_unacknowledged 字段:
sudo rabbitmqctl list_queues name messages_ready messages_unacknowledgedOn Windows, drop the sudo:
在 Windows 平台上,释放(执行)该 sudo 命令:
rabbitmqctl.bat list_queues name messages_ready messages_unacknowledged Message durability 消息持久性We have learned how to make sure that even if the consumer dies, the task isn't lost. But our tasks will still be lost if RabbitMQ server stops.
我们已经学习了如何确保即使消费者(意外)中止时,也可以让任务不会丢失。但是在 RabbitMQ 服务端停止时,我们的任务仍然会丢失。
When RabbitMQ quits or crashes it will forget the queues and messages unless you tell it not to. Two things are required to make sure that messages aren't lost: we need to mark both the queue and messages as durable.
当 RabbitMQ 退出或者崩溃时它将遗失队列和消息,除非你明确告知它不要这么做。做好两件必要的事情,也就是将队列和消息标记为可持久的,这样就可以确保消息不会遗失。
First, we need to make sure that RabbitMQ will never lose our queue. In order to do so, we need to declare it as durable:
首先,我们需要确保 RabbitMQ 不会丢失队列,为了做到这一点,我们需要声明它是可持久的:
channel.QueueDeclare(queue: "hello", durable: true, exclusive: false, autoDelete: false, arguments: null);Although this command is correct by itself, it won't work in our present setup. That's because we've already defined a queue called hello which is not durable. RabbitMQ doesn't allow you to redefine an existing queue with different parameters and will return an error to any program that tries to do that. But there is a quick workaround - let's declare a queue with different name, for example task_queue:
尽管这个命令的本身是正确的,但在我们当前的设置中仍不能正常工作,那是因为我们已经定义过一个未被持久化的名叫“hello”的队列(参照第一章提到的幂等性)。
RabbitMQ不允许采用不同参数重新定义一个已存在的队列,任何程序试图这么做的话将被返回一个错误。不过倒是有一个变通方案,让我们来声明一个不同名称的队列就好了,比如叫“task_queue”:
This queueDeclare change needs to be applied to both the producer and consumer code.