RabbitMQ默认集群原理
rabbitmq 本身是基于erlang编写,erlang语言天生具备分布式的特性(通过同步Erlang集群各节点的erlang cookie实现),RabbiteMQ天然支持集群,集群是保证可靠性的一种方式,同时可以通过水平扩展可达到增加消息吞吐量能力的目的
上图为三个节点的RabbitMQ集群,Exchange A交换器的元数据信息在所有节点上是一致的,而queue(存放消息的队列),完整数据只会存在于它所创建的节点上的,其他节点只知道队列的元数据信息和一个指向队列的owner node指针
RabbitMQ集群元数据的同步
- 队列元数据 队列名称及其属性
- 交换器元数据 交换器名称、类型、属性
- 绑定元数据 BINDINGS
- vhost元数据 为VHOST内的队列、交换器、绑定提供命名空间和安全属性
用户访问rabbitMQ节点时,通过rabbitMQctl 查询到的 queue/ user /exchange/ vhost信息基本相同
为何RabbitMQ集群仅采用元数据同步的方式
- 存储空间 如果每个集群节点都拥有所有queue的完全数据拷贝,那么每个节点的存储空间会非常大,集群的消息挤压能力会非常弱,无法通过集群节点扩容提高消息积压能力
- 性能 消息的发布者需要将消息复制到每一个集群节点,对于持久化消息,网络和磁盘的同步复制开销都会明显增加
RabbitMQ集群发送/订阅消息的基本原理
客户端连接队列的节点
消息生产者或者消费者通过amqp-client 连接至节点1进行消息的发布和订阅,那么集群中的消息收发只与节点1相关;消息生产者所连接的是节点2或节点3,此队列1的完整数据不在该两个节点上,则在发送过程中该节点主要为路由转发作用,根据这两个节点上的元数据转发至节点1上,最终发送的消息还是会存储至节点1的队列上。
集群节点类型
磁盘节点 将配置信息和元信息存储在磁盘上
内存节点 配置信息和元信息存储在内存中
RabbitMQ 要求至少有一个磁盘节点,当节点加入离开集群时,必须通知磁盘节点,唯一磁盘的磁盘节点崩溃,集群可以保持运行,但不能更改任何东西
RabbitMQ 镜像队列原理
普通集群模式,并不保证队列的高可用性,尽管交换器、绑定元数据可以复制到集群里的任何一个节点,队列内容不会复制,可以该模式解决某一项目组节点压力,但队列节点宕机直接导致该队列无法应用,只能等待重启,想要队列节点宕机或故障也可正常应用,复制队列内容到集群里的每个节点,必须创建镜像队列;镜像队列是基于普通集群模式的,镜像队列存在于多个节点,在普通集群模式上配置镜像模式实现高可用
镜像队列的结构
镜像队列基本上就是一个特殊的BackingQueue。它内部包裹了一个普通的backingQueue做本地消息的持久化处理,在此基础上增加了将消息和ACK复制到所有镜像的功能。所有对mirror-queue-master的操作,会通过可靠组播GM的方式同步到各slave节点;GM负责消息的广播,mirror-queue-salve负责回调处理,而master上的回调处理是由coordinator(协调者)负责完成。mirror-queue-salve中包含了普通的backingqueue进行消息的存储,master节点中BackingQueue 包含mirror-queue-master中由AMQQueue进行调用。
消息的发布与消费都是通过master节点完成,master节点对消息进行处理的同时将消息的处理动作通过GM广播给所有的slave节点,slave节点的GM收到消息后,通过回调交由mirror_queue_slave进行实际的处理。对于Basic.Publish,消息同时发送到master和所有slave上,如果此时master宕掉了,消息还发送slave上,这样当slave提升为master的时候消息也不会丢失。
GM
GM模块实现的一种可靠的组播通讯协议,该协议可以保证组播消息的原子性,即保证组中活着的节点要么都收到消息,要么都收不到。
将所有的节点形成一个循环链表,每个节点都会监控位于自己左右两边的节点,新增节点时,相邻节点保证当前的广播消息复制到新的节点上,当节点失效时,相邻节点会接管,保证广播消息的传递;在master与salve节点上的gm形成一个group,该信息会存储在erlang内置的mnesia中,不同的镜像队列形成不同的group,消息从master节点对于的gm发出后,顺着链表一次传送到所有的节点,循环链表,master最终会收到自己发送的消息,此时master节点已知消息复制到所有的salve节点
新增节点
每当一个节点加入或者重新加入镜像队列,之前的保存的队列内容会被清空
节点失效
某个slave失效,系统仅做记录,master依旧是master,客户端无需任何行动;若master失效,slave中的一个必须被选中为master,通常为最古老的salve(同步状态最好),master中未被同步的消息会丢失。
消息同步
将新节点加入已存在的镜像队列时,默认情况下,镜像队列中的消息不会主动同步到新节点,显式调用同步命令,当调用同步命令后,队列开始阻塞,无法操作,直到同步完毕。
总结
镜像队列,每个操作都要在所有节点做一次,降低系统性能,镜像队列数量过多,大量消息进入,集群内部的网络带宽会被这种同步通讯消耗,在高可靠性场景中使用。