RabbitMQ
有三种模式:单机模式,普通集群模式,镜像集群模式:
1.单机模式
单机模式就是说只有一台机器部署了一个 RabbitMQ
程序。这台机器宕机后就玩不转了。
2.普通集群模式
这个模式的意思就是在多台机器上启动多个 RabbitMQ
实例。类似的 master-slave
模式一样。但是创建的 queue
,只会放在一个 master rabbtimq
实例上,其他实例都同步那个接收消息的 RabbitMQ
元数据。
在消费消息的时候,如果你连接到的 RabbitMQ
实例不是存放 queue
数据的实例,这个时候 RabbitMQ
就会从存放 queue
数据的实例上拉去数据,然后返回给客户端。
总的来说,这种方式有点麻烦,没有做到真正的分布式,每次消费者连接一个实例后拉取数据,如果连接到不是存放 queue
数据的实例,这个时候会造成额外的性能开销。如果从放 queue
的实例拉取,会导致单实例性能瓶颈。
如果放 queue
的实例宕机了,会导致其他实例无法拉取数据,这个集群都无法消费消息了,没有做到真正的高可用。
所以这个事儿就比较尴尬了,这就没有什么所谓的高可用性可言了,这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue
的读写操作。
3.镜像集群模式
镜像集群模式才是真正的 RabbitMQ
的高可用模式,跟普通集群模式不一样的是:创建的 queue
无论元数据还是 queue
里的消息都会存在于多个实例上,每次写消息到 queue
的时候,都会自动把消息到多个实例的 queue
里进行消息同步。
这样的话任何一个机器宕机了别的实例都可以用提供服务,这样就做到了真正的高可用了。
但是也存在着不好之处:
- 性能开销过高,消息需要同步所有机器,会导致网络带宽压力和消耗很重
- 扩展性低:无法解决某个
queue
数据量特别大的情况,导致queue
无法线性拓展。就算加了机器,那个机器也会包含queue
的所有数据,queue
的数据没有做到分布式存储。
对于 RabbitMQ
的高可用一般的做法都是开启镜像集群模式,这样起码来说做到了高可用,一个节点宕机了,其他节点可以继续提供服务。
4. 集群架构
4.1 为什么使用集群
内建集群作为 RabbitMQ
最优秀的功能之一,它的作用有两个:
- 允许消费者和生产者在 Rabbit 节点崩溃的情况下继续运行;
- 通过增加节点来扩展 Rabbit 处理更多的消息,承载更多的业务量;
4.2 集群的特点
RabbitMQ
的集群是由多个节点组成的,但我们发现不是每个节点都有所有队列的完全拷贝。
为什么默认情况下 RabbitMQ
不将所有队列内容和状态复制到所有节点?
有两个原因:
- 存储空间——如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据。
- 性能——如果消息的发布需安全拷贝到每一个集群节点,那么新增节点对网络和磁盘负载都会有增加,这样违背了建立集群的初衷,新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
所以其他非所有者节点只知道队列的元数据,和指向该队列节点的指针。
4.3 集群异常处理
根据节点不无安全拷贝的特性,当集群节点崩溃时,该节点队列和关联的绑定就都丢失了,附加在该队列的消费者丢失了其订阅的信息,那么怎么处理这个问题呢?
这个问题要分为两种情况:
- 消息已经进行了持久化,那么当节点恢复,消息也恢复了;
- 消息未持久化,可以使用下文要介绍的双活冗余队列,镜像队列保证消息的可靠性;
4.4 集群节点类型
节点的存储类型分为两种:
● 磁盘节点
● 内存节点
磁盘节点就是配置信息和元信息存储在磁盘上,内存节点把这些信息存储在内存中,当然内存节点的性能是大大超越磁盘节点的。
单节点系统必须是磁盘节点,否则每次你重启 RabbitMQ
之后所有的系统配置信息都会丢失。RabbitMQ
要求集群中至少有一个磁盘节点,当节点加入和离开集群时,必须通知磁盘节点。
特殊异常:集群中唯一的磁盘节点崩溃了
如果集群中的唯一一个磁盘节点,结果这个磁盘节点还崩溃了,那会发生什么情况?如果唯一磁盘的磁盘节点崩溃了,不能进行如下操作:
● 不能创建队列
● 不能创建交换器
● 不能创建绑定
● 不能添加用户
● 不能更改权限
● 不能添加和删除集群几点
总结:如果唯一磁盘的磁盘节点崩溃,集群是可以保持运行的,但你不能更改任何东西。
解决方案:在集群中设置两个磁盘节点,只要一个可以,你就能正常操作。
5. 集群搭建方法
5.1 安装多个 RabbitMQ
docker run -d --hostname rabbit1 --name myrabbit1 -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.6.15-management
docker run -d --hostname rabbit2 --name myrabbit2 -p 5673:5672 --link myrabbit1:rabbit1 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.6.15-management
docker run -d --hostname rabbit3 --name myrabbit3 -p 5674:5672 --link myrabbit1:rabbit1 --link myrabbit2:rabbit2 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.6.15-management
注意点:
- 多个容器之间使用
--link
连接,此属性不能少; Erlang Cookie
值必须相同,也就是RABBITMQ_ ERLANG_ COOKIE
参数的值必须相同;
5.2 加入 RabbitMQ 节点到集群
设置节点1:
docker exec -it myrabbit1 bash rabbitmqctl stopapp rabbitmqctl reset rabbitmqctl startapp exit
设置节点 2,加入到集群:
docker exec -it myrabbit2 bash rabbitmqctl stopapp rabbitmqctl reset rabbitmqctl joincluster --ram rabbit@rabbit1 rabbitmqctl start_app exit
设置节点 3,加入到集群:
docker exec -it myrabbit3 bash rabbitmqctl stopapp rabbitmqctl reset rabbitmqctl joincluster --ram rabbit@rabbit1 rabbitmqctl start_app exit
设置好之后,使用 http://物理机 ip:15672 进行访问了,默认账号密码是 guest/guest,效果如下图:
到此为止,我们已经完成了
RabbitMQ
集群的建立,启动了 3 个节点,1 个磁盘节点和 2 个内存节点。
- 设置节点类型
如果你想更换节点类型可以通过命令修改,如下:
rabbitmqctl stop_app
rabbitmqctl changeclusternode_type dist
rabbitmqctl changeclusternode_type ram
rabbitmqctl start_app
- 移除节点
如果想要把节点从集群中移除,可使用如下命令实现:
rabbitmqctl stop_app
rabbitmqctl restart
rabbitmqctl start_app
- 集群重启顺序
集群重启的顺序是固定的,并且是相反的。
如下所述:
● 启动顺序:磁盘节点 => 内存节点
● 关闭顺序:内存节点 => 磁盘节点
最后关闭必须是磁盘节点,不然可能回造成集群启动失败、数据丢失等异常情况。
参考
https://gitbook.cn/books/5d65124b2b27dd24ed390665/index.html
https://www.cnblogs.com/vipstone/p/9362388.html