有人可以解释在发布到从节点时具有多个节点和队列的RabbitMQ集群中幕后发生的事情吗?
根据我的阅读,似乎除了发布之外的所有操作都只发送给主服务器,然后主服务器将操作的效果广播到从服务器(这来自文档)。根据我的理解,这意味着消费者将始终使用主队列中的消息。此外,如果我向从服务器发送请求以消费消息,则该从服务器将通过访问主服务器以获取该消息来执行额外的跳转。
但是当我发布到从属节点时会发生什么?此节点是否会将消息首先发送给主节点?
在处理奴隶时似乎有太多额外的跳跃,所以如果你只知道主人,你似乎可以有更好的表现。但是你如何处理主失败?然后其中一个奴隶将被选为主人,所以你必须知道连接到哪里?
要求所有这些,因为我们正在使用带有HAProxy的RabbitMQ集群,因此我们可以将集群结构与应用程序分离。这样,无论何时节点完成,HAProxy都将重定向到活动节点。但是当我们杀死其中一个兔子节点时,我们遇到了问题。与兔子的连接是永久性的,所以如果它失败了,你必须重新创建它。此外,在这种情况下,您必须重新发送消息,否则您将丢失它们。
即使所有这些,消息仍然可能丢失,因为当我杀死节点时(在某些缓冲区,网络上的某个地方等),它们可能正在传输中。因此,您必须使用事务或发布者确认,以确保在所有镜像填满消息后进行交付。但这是另一个问题。您可能有重复的消息,因为代理可能已发送一个从未到达生产者的确认(由于网络故障等)。因此,消费者应用程序需要以幂等方式执行重复数据删除或处理传入消息。
有没有办法避免这种情况?或者我必须决定是否可以丢失一些消息而不是重复某些消息?
答案 0 :(得分:14)
有人可以解释在发布到从节点时具有多个节点和队列的RabbitMQ集群中幕后发生的事情吗?
This博客概述了究竟会发生什么。
但是当我发布到从属节点时会发生什么?此节点是否会将消息首先发送给主节点?
邮件将被重定向到主队列 - 即创建队列的节点。
但是你如何处理主失败?然后其中一个奴隶将被选为主人,所以你必须知道连接到哪里?
同样,这涉及here。实质上,您需要一个单独的服务来轮询RabbitMQ并确定节点是否存活。 RabbitMQ为此提供management API。您的发布和使用应用程序需要直接或通过相互数据存储引用此服务,以确定要发布或使用的正确节点。
与兔子的连接是永久性的,所以如果它失败了,你必须重新创建它。此外,在这种情况下,您必须重新发送消息,否则您将丢失它们。
您需要订阅连接中断的事件以对断开的连接做出反应。您需要在客户端上构建一定级别的冗余,以确保消息不会丢失。如上所述,我建议您引入专门用于询问RabbitMQ的服务。您的客户端可以尝试将消息发布到上次已知的活动连接,如果失败,客户端可能会向监视器服务请求RabbitMQ群集的最新列表。假设至少有一个活动节点,客户端可以建立与它的连接并成功发布消息。
即使有了所有这些,消息仍然会丢失,因为当我杀死一个节点时它们可能正在传输
有些边缘情况你无法覆盖冗余,RabbitMQ也不能。例如,当消息登陆队列时,HA策略调用后台进程将消息复制到备份节点。在此过程中,消息可能会在保留到备份节点之前丢失。如果活动节点立即失败,则消息将永久丢失。对此无能为力。不幸的是,当我们达到通过网络传输的实际字节数时,我们可以构建的安全措施数量有限。
因此,消费者应用程序需要以幂等方式执行重复数据删除或处理传入消息。
您可以通过多种方式处理此问题。例如,将message-ttl
设置为相对较低的值将确保重复的消息不会长时间保留在队列中。您还可以使用唯一引用标记每条消息,并在使用者级别检查该引用。当然,这需要存储已处理消息的缓存以比较传入消息;这个想法是,如果先前处理过的消息到达,消费者会缓存其标签,并且可以忽略该消息。
我一直强调的一点是,基于AMQP和基于队列的解决方案是您的基础架构提供的工具,而不是整个解决方案。您必须根据业务需求弥补这些差距。通常,最好的解决方案是通过反复试验得出的。我希望我的建议有用。我在这里写了一些关于RabbitMQ设计解决方案的博客,包括你提到的问题here,如果你有兴趣的话。