RabbitMq Acks / 重新发布

Posted

技术标签:

【中文标题】RabbitMq Acks / 重新发布【英文标题】:RabbitMq Acks / Republishing 【发布时间】:2015-12-30 15:36:32 【问题描述】:

处理使用 Rabbit 的 C# 项目。我在文档中发现了关于何时由于连接或通道死亡(是哪个?)而重新传递消息的相互矛盾的信息。

这里的文档: http://www.rabbitmq.com/semantics.html

声明当频道关闭时重新排队发送

可以使用 AMQP 方法将消息返回到队列 重新排队参数(basic.recover、basic.reject 和 basic.nack),或 由于通道关闭,同时持有未确认的消息。任何 这些场景导致消息在后台重新排队 早于 2.7.0 的 RabbitMQ 版本的队列。从 RabbitMQ 发布 在 2.7.0 中,消息始终按发布顺序保留在队列中,即使存在重新排队或通道关闭。

但是在这里:http://www.rabbitmq.com/tutorials/tutorial-two-dotnet.html

状态:仅当工作连接终止时

如果消费者没有发送确认就死了,RabbitMQ 会理解 消息未完全处理并将重新发送给另一个 消费者。这样您就可以确保不会丢失任何消息,即使 工人偶尔会死。

没有任何消息超时; RabbitMQ 将重新传递消息 仅当工作人员连接中断时。即使处理一个也没关系 消息需要非常非常长的时间。

那么实际上什么时候重新交付?当工人或渠道死亡?我可以在一个频道上消费而在另一个频道上ACK吗?

目前我创建了一个 ChannelManager 类,它打开 N 个通道并将它们存储在 ConcurrentQueue 和 Queues / Dequeues Channels 中,并确保我们永远不会低于“最小”可用通道数。使用这种方法,我无法确保 Consume 和 Ack 发生在同一个频道上......

【问题讨论】:

我已经提交了一个问题:github.com/rabbitmq/rabbitmq-website/issues/88 关于第二个问题:“我可以在一个通道上消费但在另一个通道上进行 ACK 吗?” -> 不,你不能。 【参考方案1】:

第二个引用的措辞不正确,尽管该工作页面周围的上下文仍然正确...您用粗体输入的那句话将正确更改为:

当消费者死亡时,RabbitMQ 会重新传递消息。

这样说不会排除其他合法案例,但会说明他们在本文中提出的观点。

...

如果您希望消息重新排队并重新传递,您必须确保 noAck 设置为 false(这是默认设置)。一旦你有了这个,你引用的第一段就是正确的。

...

关于您的ChannelManager - 没必要。渠道是便宜、快捷的东西,可以根据需要竖起和拆除。只要您使用所有开放频道,就可以只有 1 个开放频道或拥有 1,000 个开放频道。

另一方面,连接很昂贵。为每个应用程序实例打开一个连接。然后在该连接中使用尽可能多的频道。

【讨论】:

【参考方案2】:

首先,我称之为“通道池”会给您的应用程序增加有害的复杂性。通道绝对不能在线程之间共享。将通道视为端点标识符 - 它并不代表与代理的单独连接,而是通过连接与代理进行不同的讨论 - read more here。

其次,教程并不是真正的文档。它有助于理解代理的工作原理,但我通常希望作为教程的一部分编写的内容特定于该教程中描述的场景。在这种情况下,文档解释了消息可能被重新排队的情况。

第三,重新排队和重新交付是分开的事情。重新交付只能在重新排队后发生,但不能保证。例如,一条消息可能在重新排队之后但在重新传递之前过期(可能是因为没有可用的消费者来获取消息,或者它在队列中等待的时间过长)。

最后,每条传递给消费者的消息都会被发布一个传递标签。该标签特定于频道和频道上的消费者。因此,不可能从另一个频道ack,因为另一个频道缺乏传递标签的上下文感知。

【讨论】:

以上是关于RabbitMq Acks / 重新发布的主要内容,如果未能解决你的问题,请参考以下文章

RabbitMQ延迟队列简单示例

Erlang 机器立即停止(分发名称冲突?)。服务未重新启动,因为 OnFail 设置为忽略

JAVA秒杀系统的简单实现(redis+rabbitmq)

rabbitmq配置

rabbitmq简介

RabbitMQ 笔记-工作队列