8、RabbitMQ的集群原理

集群架构

写在前面

RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。若是必定要在高延迟环境下使用RabbitMQ集群,能够参考使用Shovel和Federation工具。安全

RabbitMQ社区中的传统观念要求集群中节点数量的上限在32至64个,由于每向集群添加一个节点,就添加了同步的复杂性。集群中的每一个节点必须知道其余节点的信息,这种非线性的复杂度会拖慢消息投递和集群管理。bash

集群中的队列

RabbitMQ集群设计目的有两个:网络

  • 容许消费者和生产者在RabbitMQ节点崩溃的状况下继续运行;
  • 经过添加更多的节点来线性扩展消息通讯的吞吐量;

当一个RabbitMQ集群节点崩溃时,该节点上队列的消息也会消失。这事由于RabbitMQ默认不会将队列的内容复制到整个集群上。若是不进行特别的配置,这些消息仅存在于队列所属的那个节点上。架构

RabbitMQ会始终记录如下四种类型的内部元数据:工具

  • 队列元数据:队列名称和它们的属性;
  • 交换器元数据:交换器名称、类型和属性;
  • 绑定元数据:一张简单的表格展现了如何将消息路由到队列;
  • vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;

当你引入集群时,RabbitMQ须要追踪新的元数据类型 —— 集群节点位置,以及节点与已记录的其余类型元数据的关系。集群也提供了将元数据存储到磁盘或内存中的选项。性能

不是每个节点都有全部队列的彻底拷贝。 在集群中建立队列的话,集群只会在单个节点(而不是全部节点)上建立完整的队列信息。 结果是,只有队列的全部者节点,知道有关队列的全部信息。 其余非队列全部者的节点们,只知道队列的元数据和指向队列存在的那个节点的指针。 所以,当集群节点崩溃时,该节点的队列和关联的绑定就都消失了。 附加在那些队列上的消费者丢失了订阅。 而且任何匹配该队列绑定信息的新消息也丢失了。

若是消费者要消费的队列的所在节点故障了,而该队列是持久化的,那么想要继续消费,惟一的办法就是恢复故障节点。当失败节点恢复后加入集群,该节点上的队列消息不会丢失。spa

尝试在持久化队列节点故障后,从新声明队列,会获得一个404 NOT_FOUND错误。但若是队列不是持久化的,那么从新声明就会成功。插件

RabbitMQ设计上不将队列内容和状态复制到全部的节点上,主要有两个缘由:设计

存储空间 —— 若是一个节点能够存储1GB的消息,那么在多个节点之间复制消息则会浪费多个G的空间;指针

性能因素 —— 消息的发布须要将消息复制到每个节点,对于持久化消息来讲,每次都会触发磁盘活动,每次新增节点,网络和磁盘负载都会增长;

另外,在向队列发送消息时,只有队列的全部节点,才会收到磁盘操做的影响。其余非全部节点,只须要将接收到的消息传递给全部者节点便可。

所以,往Rabbit集群中添加更多的节点,就意味着将拥有更多的节点来传播消息,多个节点会带来性能的提高。

集群中的交换器

交换器,本质上就是一个名称和队列绑定的列表。

当你将消息发布到交换器时,其实是由链接的信道,根据消息上的路由键,去交换器绑定列表中进行比较,而后路由消息到队列中。

因为交换器只不过是一张表,所以将这张表在集群中复制很简单,当建立一个新的交换器时,RabbitMQ所要作的是将交换器添加到集群中的全部节点上。

集群的消息可靠性与单点模式基本一致。AMQP的Basic.Publish命令不会返回消息的状态。这意味着当信道节点崩溃时,信道可能仍然在路由消息,解决方案是使用事务,或发布确认模式。

集群节点类型

RabbitMQ的集群大体上可分为3类:

  • 磁盘节点;
  • 内存节点;
  • 统计节点;

当建立集群时,至少保证一个磁盘节点和若干个内存节点。不管哪一种节点,都不会影响消息持久化。

当有多个磁盘节点时,就能在发生硬件故障时更加游刃有余。

若存在多个磁盘节点,在发生故障恢复时可能会出现状态不一致的状况,这种时候,可关闭集群并按照顺序从新启动节点。

统计节点,只能和磁盘节点搭配使用。它负责收集集群中每一个节点的所有统计数据和状态数据。在任什么时候候,一个集群只能有一个统计节点。

在拥有两个磁盘节点的集群中,若是主节点发生故障,统计节点将指派给备用的磁盘节点。

集群的节点搭配

若是只有一个磁盘节点,当这个节点发生故障后,集群仍是能够继续路由消息,可是不能作如下操做:

  • 建立队列;
  • 建立交换器;
  • 建立绑定关系;
  • 添加用户;
  • 更改权限;
  • 添加或删除集群节点;

最好是设置一个以上的磁盘节点,在某个磁盘节点故障时,还有备用的磁盘节点可用。

当内存节点添加到集群或从新加入到集群时,他们会链接到预先配置的磁盘节点,下载集群元数据。当添加内存节点时,确保告知该内存节点全部的磁盘节点。

集群节点升级

集群的升级是半自动化的,在没有准备的状况下解压新版本的RabbitMQ覆盖旧版本,会抹去集群上全部配置和数据。若是要保留这些,须要进行一些操做。

首先,经过Management插件备份配置;

而后,关闭全部生产者并等待全部消费者消费完队列中全部的消息(使用rabbitmqctl观察队列状态);

接着,关闭节点并解压新版本的RabbitMQ;

随后,选择其中一个磁盘节点做为升级节点,当它启动时,该节点会将持久化的集群数据升级到新版本,而后在启动其余的磁盘节点;

最后,启动内存节点,这样就会让集群中运行新版本的RabbitMQ了,并且元数据都会保留;

镜像队列

在RabbitMQ2.6版本后,RabbitMQ团队给集群带来了内建的双冗余选项:镜像队列。

想普通队列那样,镜像队列的主拷贝仅存在于一个节点(主队列,Master)上,但与普通队列不一样的是,镜像节点在集群中的其余节点上拥有从队列(slave)拷贝。一旦队列主节点不可用,最老的从队列将被选举为主队列。

在定义队列时,加入参数:

x-ha-policy = all

这意味着队列被镜像到集群中的全部节点上。若是在该队列声明以后,集群又新增了节点,那么该节点也会自动托管一份队列的从拷贝。

镜像队列原理

消息发送到队列时,若是队列是镜像队列,那么也要将消息投递到镜像队列的从拷贝。

想确保消息没有丢失的话,同样可使用发布确认模式,不一样的是,RabbitMQ会在全部队列安全的接收了消息后在通知你。

这里须要有从发布者和消费者两方面说一下异常状况:

  • 发布者:若是消息在路由到从拷贝前,主拷贝发生了故障,而且从拷贝升级成了主拷贝,这种状况下,发布确认永远不会到达;
  • 消费者:若是镜像队列失去一个节点,则附加在镜像队列上的任何消费者都不会注意到这同样。可是若是主拷贝节点发生故障,那么全部该节点的消费者须要从新附加并监听新的队列主拷贝;
注意:若是消费者链接在从拷贝,当主拷贝发生变化时,RabbitMQ会发送给客户端一个Consumer Cancellation的通知,告知客户端发生了变化。若是你的客户端不支持这个通知处理的话,你的程序会空跑,觉得队列中没有消息能够消费了。 若是你的客户端不支持这个消费者取消通知处理,那么应该避免使用镜像队列!!!
相关文章
相关标签/搜索