背景:RabbitMQ这款消息队列中间件产品本身是基于Erlang编写,Erlang语言天生具备分布式特性(通过同步Erlang集群各节点的magic cookie来实现)。因此,RabbitMQ天然支持Clustering。这使得RabbitMQ本身不需要像ActiveMQ、Kafka那样通过ZooKeeper分别来实现HA方案和保存集群的元数据。集群是保证可靠性的一种方式,同时可以通过水平扩展以达到增加消息吞吐量能力的目的。 RabbitMQ分布式部署有3种方式:集群、Federation(联邦)和Shovel(铲)。这三种方式并不是互斥的,可以根据需求选择相互组合来达到目的,后两者都是以插件的形式进行设计,复杂性相对高,此篇只聊一下RabbitMQ自带的内建集群。下面先来看下三个节点组成了一个RabbitMQ集群:Exchange(交换器)的元数据,Queue(队列)的元数据信息在所有节点上是一致的而Queue的完整数据则只会存在于它所创建的那个节点上。其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。 队列所在的节点称为宿主节点:队列创建时,只会在宿主节点创建队列的进程,宿主节点包含完整的队列信息,包括元数据、状态、内容等等。因此,只有队列的宿主节点才能知道队列的所有信息。队列创建后,集群只会同步队列和交换器的元数据到集群中的其他节点,并不会同步队列本身,因此非宿主节点就只知道队列的元数据和指向该队列宿主节点的指针。 场景1、客户端直接连接“队列所在节点broker”如果有一个消息生产者或者消息消费者通过amqp-client的客户端连接至节点brokerA进行消息的发布或者订阅,那么此时的集群中的消息收发只与节点brokerA相关,这个没有任何问题;如果客户端相连的是节点B或者节点C(队列A数据不在该节点上),那么情况又会是怎么样呢?场景2、客户端连接的是“非队列数据所在节点broker”如果消息生产者所连接的是节点B或者节点C,此时队列A的完整数据不在该两个节点上,那么在发送消息过程中这两个节点主要起了一个路由转发作用,根据这两个节点上的元数据(也就是上文提到的:指向queue的owner node的指针)转发至节点A上,最终发送的消息还是会存储至节点A的队列A上。优缺点优点:这样的设计,保证了不论从哪个broker中均可以消费所有队列的数据,并分担了负载,因此,增加broker可以线性提高服务的性能和吞吐量。缺点:但该方案也有显著的缺陷,那就是不能保证消息不会丢失。当集群中某一节点崩溃时,崩溃节点所在的队列进程和关联的绑定都会消失,附加在那些队列上的消费者也会丢失其订阅信息,匹配该队列的新消息也会丢失。崩溃节点重启后,需要从磁盘节点中同步元数据信息,并重建队列,所以,集群中要求必须至少有一个broker为磁盘节点,以保证集群的可用性。解决消息可能丢失的问题RabbitMQ采用镜像队列的方式保证队列的可靠性。镜像队列就是将队列镜像到集群中的其他节点,如果集群中一个节点失效了,队列就能自动的切换到镜像中的节点,一个镜像队列中包含有1个主节点master和若干个从节点slave。其主从节点包含如下几个特点:消息的读写都是在master上进行,并不是读写分离master接收命令后会向salve进行组播,salve会命令执行顺序执行master失效,根据slave加入的时间,最老的slave会被提升为master互为镜像的是队列,并非节点,集群中可以不同节点可以互为镜像队列,也就是说队列的master可以分布在不同的节点上
|