Rabbit MQ的分布式部署方式总共有三种,分别是集群部署、Federation(联邦)部署 和 Shovel部署。有趣的是,这三种部署方式并不是互斥的,而是可以联合使用的。用户可以根据实际情况,选择其中的一种或多种部署方式来满足自己的实际应用需求。这些部署方式的联合使用固然提高应用程序的性能和灵活性等,但同时也提高了部署的复杂性。
三种部署方式各有其优缺点,需要根据具体的业务场景确定具体的部署方式,现在就来具体看一下集群部署方式的应用场景和底层实现吧。
在了解集群部署之前,先来介绍一下集群的概念吧。
集群就是将多个服务器部署在同一个网络区间内,集群的多个服务器可以看成是一个整体,一个逻辑上的服务器,因此集群可以提高应用程序的吞吐量和可靠性。
介绍完集群的概念后,现在就来看一下今天的主角,Rabbit MQ集群部署吧。
那么什么时候需要用到集群部署呢,就是当你服务器抗不住你应用的程序的吞吐量时,比如你的应用程序一秒几十万的消息吞吐量,或者是更大,这个时候就需要用到Rabbit MQ的集群部署了。
Rabbit MQ的集群模式共分为以下两种:
Rabbit MQ的集群配置方式也有三种:
普通集群模式就是将多台Rabbit MQ服务器连接组成一个集群,在连接过程中需要正确的Erlang Cookie和节点名称才能保证机器之间相互进行连接访问,并且集群需要要局域网内进行部署。
集群中的每一台服务器可以说成是集群的一个节点,看过前面文章的同学应该知道,每一个Rabbit MQ服务器都是由连接池、信道、交换机、队列等组成,Rabbit MQ服务器的结构组成如下图所示:
但是Rabbit MQ的集群不是每个节点都有所有队列的完全拷贝。从上面的图中也可以看出,交换机A的的元数据信息在所有节点上都是一致的,但是存放消息的队列的完整信息都只存在它所创建的节点上,所有其他节点只知道队列的元数据和指向该队列存在的那个节点的指针,元数据信息包括以下内容:
思考一个问题:为什么Rabbit MQ不把所有数据拷贝到所有节点上呢?而是只拷贝元数据信息呢?
原因有两个:
以三个节点(node1、node2、node3)的集群为例来进行说明。消息实体是存在于队列之中的,而节点之间只有相同的元数据信息,假设消息存在于node1节点的A队列上,当消费者从从node2节点上的B队列消费时,这时RabbitMQ会临时在node1和node2节点进行消息传输,把A队列上的消息实体传到B队列上,然后发送给消费者。
这个过程其实会对node1节点产生性能瓶颈,因为无论consumer连node1或node2,都会从node1拉取数据。针对这种情况,有一个中庸的做法就是将consumer尽量连接每一个节点。
集群几点类型分为以下两类:
磁盘节点的数据信息是存储在磁盘上的,内存节点的信息是存储在内存上的,因此内存节点的性能要高于磁盘节点。
注意: Rabbit MQ要求集群中至少有一个磁盘节点,所有其他节点可以是内存节点,当节点加入和离开集群时,必须通知磁盘节点。
当惟一的磁盘节点奔溃时,
也就是说,如果集群中唯一的磁盘节点崩溃,集群仍然可以保持运行,但是直到将该节点恢复到集群前,你无法更改任何东西。所以在建立集群的时候应该保证有两个或者多个磁盘节点的存在。
当集群节点崩溃时,该节点的队列进程和关联的绑定都会消失。附加在那些队列上的消费者 会丢失其所订阅的信息 井且任何匹配该队列绑定信息的新消息也都会消失。那么面临这种情况应该如何处理呢:
多机集群提高了系统的吞吐量和可靠性。但是并没有做到高可用性,因为当磁盘节点崩溃的话,其它节点不能进行创建队列、创建交换器等,可以这样说吧,其它内存节点就是为磁盘节点服务的,下面介绍的镜像模式部署解决了这个缺点,实现了集群的高可用性。
镜像集群模式其实就是把需要的队列做成镜像队列,然后将镜像队列放在多个节点当中,这种镜像集群模式解决了普通集群模式没有做到的高可用性的缺点,镜像集群模式属于Rabbit MQ的高可用性的集群部署方案。
镜像集群模式的结构如下图所示:
其中master是主节点(存放消息实体的队列),slave是从节点(镜像队列),一个主节点可以有多个从节点,消息实体 经过GM(Guaranteed Multicast)协议在主从镜像节点之间进行广播同步,这样无论哪一台服务器节点宕机了,其它服务器节点照样可以工作,它们的关系如下图所示:
那么当master节点崩溃后,会发生什么呢?什么都不会发生发生,因为还有slave节点啊,slave节点会保存消息体,
Rabbit MQ规定,当master节点宕机后,“资历最老"的 slave 会被提升为新的 master,根据 slave 加入的时间排序,时间最长的 slave 即为"资历最老”。
当消费者与master队列建立连接,消费者可以直接从master队列上获取信息,当消费者与slave队列建立连接呢?消费者是从slave队列直接获取数据的吗?当然不是的,消息的流转顺序如下所示:
那这样就会有一个疑问?消费者的请求都是由master队列进行处理的,那么消息的负载是不是不能够做到有效的均衡呢?
Rabbit MQ的负载均衡是体现在物理机器层面上的,而不是体现在内存中的队列层面的。这样解释吧,现在有3台物理机,需要创建3个master队列和6个slave队列, 消息的请求负载都在3个master队列上,那么只需要将3个master队列和6个slave队列均匀的分布在3台物理机上,这样在很大程度上实现了每台机器的负载均衡。当然每个master队列消息请求的数量可能会有不同,无法保持绝对的负载均衡。
那么镜像队列是怎么保证消息传输的可靠性呢?
RabbitMQ的镜像队列使用 publisher confirm 和事务两种机制来保证其消息的可靠性。在事务机制中,只有当前事务在全部镜像中执行之后,客户端才会收到 Tx Commit-Ok 的消息。同样的,在 publisher confirm 机制中,生产者进行当前消息确认的前提是该消息被全部进行所接收了。
镜像队列的引入可以极大地提升 RabbitMQ 的可用性及可靠性,提供了数据冗余备份、避免单点故障的功能,因此推荐在实际应用中为每个重要的队列都配置镜像。
说了这么多的镜像队列的优点,那么镜像队列就没有缺点了吗?当然不是,那么镜像集群的缺点是什么呢?
欢迎各位关注我的JAVAERS公众号,陪你一起学习,一起成长,一起分享JAVA路上的诗和远方。在公众号里面都是JAVA这个世界的朋友,公众号每天会有技术类文章,面经干货,也有进阶架构的电子书籍,如Spring实战、SpringBoot实战、高性能MySQL、深入理解JVM、RabbitMQ实战、Redis设计与实现等等一些高质量书籍,关注公众号即可领取哦。
如果大家对人工智能感兴趣,可以关注下面公众号,会持续更新c++、python、tensorflow、机器学习、深度学习、计算机视觉等系列文章