RabbitMQ–扩展–01–集群原理
1、默认集群模式(普通集群模式)
1.1、架构
- 采用三个节点组成了一个RabbitMQ的集群
- Exchange A(交换器)的元数据信息在所有节点上是一致的
- Queue(存放消息的队列)的完整数据则只会存在于它所创建的那个节点上,其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。
- 内容仅仅维持在单个节点之上,所以一个节点的失效表现为其对应的队列不可用。
1.2、RabbitMQ集群元数据的同步
RabbitMQ集群会始终同步四种类型的内部元数据:
- 队列元数据:队列名称和它的属性
- 交换器元数据:交换器名称、类型和属性
- 绑定元数据:一张简单的表格展示了如何将消息路由到队列
- vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性
因此,当用户访问其中任何一个RabbitMQ节点时,通过rabbitmqctl查询到的queue/user/exchange/vhost等信息都是相同的。
1.3、为什么 RabbitMQ 集群只使用元数据同步?
1.3.1、存储空间。
假设每个集群节点都有所有Queue的完整数据副本,会有以下影响
- 那么每个节点的存储空间会非常大
- 集群的消息积压能力会很弱
- 消息积压能力无法通过扩容集群节点来提升
1.3.1、性能
- 消息的发布者需要将消息复制到每个集群节点。
- 对于持久化消息,网络和磁盘同步复制的开销会显着增加。
1.4、优缺点
1.4.1、优点
- 集群每个节点的存储空间不会很大
- 集群的消息积压能力会很强,消息积压能力可以通过扩容集群节点来提升
- 集群的性能更高
1.4.12、缺点
1.4.12.1、不保证队列的高可用
- 虽然交换机和绑定可以复制到集群中的任何节点,但不会复制队列的内容
- 如果队列节点的宕机,会直接导致队列不可用,只能等待重启
2、RabbitMQ集群发送/订阅消息的基本原理
2.1、RabbitMQ集群工作原理图
- 节点1 有 队列1的完整数据
- 节点2和节点3 没有 队列1的完整数据
2.2、客户端直接连接队列所在节点
如果消息生产者(消费者)通过amqp-client的客户端 连接到 节点1 发送(消费)消息,那么此时集群中的消息发送(消费)只与节点1相关。
2.3、客户端连接到非队列数据所在节点
- 如果消息生产者连接到节点2或节点3,队列1的完整数据不在这两个节点上,那么这两个节点在发送消息的过程中主要起到路由转发的作用,根据这两个节点上的元数据被转发到节点1,最终发送的消息仍然会存储在节点1的队列1中。
- 如果消息消费者连接的节点2或节点3,这两个节点也会充当路由节点转发消息,消息会从节点1的队列1中拉取消费。
2.4、集群节点类型
- RabbitMQ 要求集群中至少有一个磁盘节点。当节点加入和离开集群时,必须通知磁盘节点
- 如果集群中唯一的磁盘节点崩溃,则不能创建队列、创建交换机、创建绑定、添加用户、更改权限、添加和删除集群节
- 总之,如果唯一磁盘的磁盘节点崩溃,集群可以继续运行,但什么都不能改变。因此,建议在集群中设置两个磁盘节点,只要一个可用,就可以正常运行。
2.4.1、disc(硬盘)
- 采用disc,则需要对exchange,queue,delivery mode 设置成durable模式。
- 好处:当RabbitMQ失效了,message仍然可以在重启之后恢复。
- 在磁盘上存储配置信息和元信息
- 单节点系统必须是磁盘节点,否则每次重启RabbitMQ都会丢失所有系统配置信息
2.4.2、ram(内存)
- 处理message的效率比:
- ram:disc=3:1
- 如果有其它HA手段保障的情况下,选用ram方式是可以提高消息队列的工作效率的。
- 使用ram方式,RabbitMQ能够承载的访问量则取决于可用的内存大小。
- 在内存中存储配置信息和元信息
3、镜像集群模式
- 解决了普通集群模式没有做到的高可用性的缺点
- 引入镜像队列(Mirror Queue)的机制,可以将 队列镜像 放在 集群中的其他Broker节点之上,如果集群中的一个节点失效了,队列能够自动切换到镜像中的另一个节点上以保证服务的可用性
- 镜像队列存在于多个节点上
3.1、实现镜像模式 步骤
- 需要先搭建一个普通的集群模式
- 然后在这个模式的基础上配置镜像模式,实现高可用。
3.2、每个镜像队列结构
- 每个镜像队列结构都包含一个主节点(master)和若干个从节点(slave)
- 上图是master和slave组成了一个链表结构,也是镜像队列结构。
- slave会准确地按照maste执行命令地顺序进行动作,故slave和master上维护的状态应该是相同的。如果master由于某种原因失效,那么"资历最老"(基于slave加入cluster的时间排序)的slave会被提升为新的master。发送到镜像队列的所有消息会被同时发往 master和所有的slave上,如果此时master挂掉了,消息还会在slave上,这样slave提升为 master的时候消息也不会丢失
3.3、镜像集群 结构
使用3台机器构建集群,具有复制、高可用和分片特性,当master节点挂了,slave节点会选举为master节点,继续提供服务。
集群模式可以避免单个节点成为性能瓶颈,且支持读写分离。
Redis集群将整个数据库分为16384个槽,数据库中的每个键都属于16384个槽中的其中一个,集群中的每个master节点都可以负责0个至16384个槽,master节点只会执行和自己负责的槽有关的命令,当master节点接收到不属于自己处理的槽的命令时,它将会处理指定槽的节点的地址返回给客户端,而客户端会向正确的节点重新发送
3.3.1、Broker 节点
集群中的每个 Broker 节点都包含 1 个队列的 master 和 2 个队列的 slave
3.3.2、负载
- Q1队列 的负载大多都集中在 broker1 上
- Q2队列 的负载大多都集中在 broker2 上
- Q3队列 的负载大多都集中在 broker3 上
只要确保队列的 master 节点均匀散落在集群中的各个 Broker 节点即可确保很大程度的负载均衡。
3.3.3、master和slave
- master提供读写服务,在slave上的操作都会路由到master上
- slave只做备份-主备切换
4、注意点
4.1、在广域网中不应该使用集群
- RabbitMQ 集群对延迟非常敏感,应当只在本地局域网内使用。
- 在广域网中不应该使用集群,而应该使用Federation 或者Shove1 来代替。
4.2、什么是cookie
- cookie 相当于密钥令牌,集群中的RabbitMQ 节点需要通过交换密钥令牌以获得相互认证
- 如果节点的密钥令牌不一致,那么在配置节点时就会有如下的报错,注意字体加粗部分。
4.3、集群环境,对cookie的要求
- 集群环境,需要确保各个节点的cookie 文件使用的是同一个值。
- 可以读取 其中一个节点的cookie值, 然后将其复制到 其他节点中。
- cookie 文件默认路径
- /var/lib/rabbitmq/.erlang.cookie
- $HOME/.erlang.cookieo