集群中的队列

从直觉上来看,一说到集群,我们就联想到高可用,一个节点宕机了,不会影响整体服务,客户端会从其他节点拿数据。比如 ES, Redis, mongoDB 等的集群都是符合直觉的架构。

但是,事情到了rabbitMQ 这里,却完全不一样了。

在将两个节点组成集群的那一刻,事情发生了巨大的变化:不是每一个节点都有所有队列的完全拷贝。

在单一节点设置中,所有关于队列的信息(元数据、状态和内容)都完全存储在该节点上。但是如果在集群中创建队列的话,集群只会在单个节点而不是在所有节点上创建完整的队列信息(元数据、状态、内容)。结果是只有队列的所有者节点知道有关队列的所有信息。所有其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。因此当集群节点崩溃时,该节点的队列和关联的绑定就都消失了。附加在那些队列上的消费者丢失了其订阅信息,并且任何匹配该队列绑定信息的新消息也都丢失了。

宕机后的队列

不要担心, 节点宕机后,我们可以让消费者重新连接到集群中并重新创建Q队列, 如果队列Q再一开始就是内存级别的,也就说我们可以承受节点宕机后,队列内容丢失的风险,重新创建一个全新的队列也没有什么不可。

但是如果队列Q一开始是被设置成了持久化的呢?我们希望队列中的数据更可靠,不能重启节点就丢失了。所以如果允许重新创建队列,不就是把之前的Q队列给覆盖了吗? rabbit 已经考虑这种情况, 所以在节点恢复之前, 不允许在集群中重新声明同名队列了, 如果非要这么做的话,你会得到一个 404 NOT_FOUND 的错误。除非将节点恢复,这种机制保证了持久化的队列中的消息不会丢失。

产生上面现象的 “罪魁祸首” 就是因为,默认情况下,队列只存在单一节点上而不是每个节点丢复制一份呢?

rabbit 这样设计肯定时有道理的嘛!但是为什么呢?

一切为了性能

(1)存储空间——如果每个集群节点都拥有所有队列的完整拷贝,那么添加新的节点不会给你带来更多存储空间。举个例子,如果一个节点可以存储1GB的消息,那么添加两个节点只会给你带来两个一模一样的1GB消息的拷贝。

(2)性能——消息的发布需要将消息复制到每一个集群节点。对于持久化消息来说,每一条消息都会触发磁盘活动。每次新增节点,网络和磁盘负载都会增加,最终只能保持集群性能的平稳(甚至更糟)。

通过设置集群中的唯一节点来负责任何特定队列,只有该负责节点才会因队列消息而遭受磁盘活动的影响。所有其他节点只需要将接收到的该队列的消息传递给该队列的所有者节点。因此,往Rabbit集群添加更多的节点意味着你将拥有更多的队列,这些新增节点为你带来了性能的提升。当负载增加时,RabbitMQ集群是性能扩展的最佳方案。