一、介绍

在前几篇文章中,我们详细的介绍了 RabbitMQ 的内部结构和使用,以及 SpringBoot 和 RabbitMQ 整合,都是基于单台 RabbitMQ 进行使用的。

我们知道在微服务流行的当下,一旦单台服务器挂了,基本上就无法提供高可用的服务了,因此为了保证服务高可用,在生产环境上我们通常的做法是搭建一个 RabbitMQ 集群,即使某台 RabbitMQ 故障了,其他正常的 RabbitMQ 服务器依然可以使用,应用程序的持续运行不会受到影响。

二、集群架构原理

在前几篇文章中,我们有介绍到 RabbitMQ 内部有各种基础构件,包括队列、交换器、绑定、虚拟主机等,他们组成了 AMQP 协议消息通信的基础,而这些构件以元数据的形式存在,它始终记录在 RabbitMQ 内部,它们分别是:

  • 队列元数据:队列名称和它们的属性
  • 交换器元数据:交换器名称、类型和属性
  • 绑定元数据:一张简单的表格展示了如何将消息路由到队列
  • vhost 元数据:为 vhost 内的队列、交换器和绑定提供命名空间和安全属性

这些元数据,其实本质是一张查询表,里面包括了交换器名称和一个队列的绑定列表,当你将消息发布到交换器中,实际上是将你所在的信道将消息上的路由键与交换器的绑定列表进行匹配,然后将消息路由出去。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9sYUVtaWJIRnhGdzd6c2xNN1pZczVTd0FzZXQzNDB2RmxpYXdqUHM3bDZaUTF2VWp2bzVxOElpYTNJWGJRRDdkdTRuc3gyUExTRERpYWttYWliMGNzMG5MUXF3LzY0MA.jpeg

消息路由表
有了这个机制,那么在所有节点上传递交换器消息将简单很多,而 RabbitMQ 所做的事情就是把交换器元数据拷贝到所有节点上,因此每个节点上的每条信道都可以访问完整的交换器。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9sYUVtaWJIRnhGdzd6c2xNN1pZczVTd0FzZXQzNDB2RmxVaWFqY0VPY2ZhYmljbURpYzRPVlNKdnN2ejlxaWMwdklFNmdIWFVtR3VzV2NWbVVEOFlxeGFlM2tBLzY0MA.jpeg

如果消息生产者所连接的是节点 2 或者节点 3,此时队列1的完整数据不在该两个节点上,那么在发送消息过程中这两个节点主要起了一个路由转发作用,根据这两个节点上的元数据转发至节点1上,最终发送的消息还是会存储至节点1的队列1上。

同样,如果消息消费者所连接的节点2或者节点3,那这两个节点也会作为路由节点起到转发作用,将会从节点1的队列1中拉取消息进行消费。

与常见的集群主从架构模式不同的地方在于:RabbitMQ 集群模式下,仅仅只是同步元数据,每个队列内容还是在自己的服务器节点上

这么设计主要还是基于集群本身的性能和存储空间上来考虑:

  • 存储空间:真正存放数据的地方是在队列里面,如果每个集群节点都拥有所有队列的完全数据拷贝,那么每个节点的存储空间会非常大,集群的消息积压能力会非常弱。例如你现在存储了 3G 队列内容,那么在另外一个只有 1G 存储空间的节点上,就会造成内存空间不足的情况,也就是无法通过集群节点的扩容提高消息积压能力。
  • 性能:消息的发布者需要将消息复制到每一个集群节点,每一条消息都会触发磁盘活动,这会导致整个集群内性能负载急剧拉升。

既然每个队列内容还是在自己的服务器节点上,同样也会带来新的问题,那就是如果队列所在服务器挂了,那存在服务器上的队列数据是不是全部都丢失了?

在单个节点上,RabbitMQ 存储数据有两种方案:

  • 内存模式:这种模式会将数据存储在内存当中,如果服务器突然宕机重启之后,那么附加在该节点上的队列和其关联的绑定都会丢失,并且消费者可以重新连接集群并重新创建队列;
  • 磁盘模式:这种模式会将数据存储磁盘当中,如果服务器突然宕机重启,数据会自动恢复,该队列又可以进行传输数据了,并且在恢复故障磁盘节点之前,不能在其它节点上让消费者重新连到集群并重新创建队列,如果消费者继续在其它节点上声明该队列,会得到一个 404 NOT_FOUND 错误,这样确保了当故障节点恢复后加入集群,该节点上的队列消息不会丢失,也避免了队列会在一个节点以上出现冗余的问题。

在集群中的每个节点,要么是内存节点,要么是磁盘节点,如果是内存节点,会将所有的元数据信息仅存储到内存中,而磁盘节点则不仅会将所有元数据存储到内存上, 还会将其持久化到磁盘

在单节点 RabbitMQ 上,仅允许该节点是磁盘节点,这样确保了节点发生故障或重启节点之后,所有关于系统的配置与元数据信息都会从磁盘上恢复。

而在 RabbitMQ 集群上,至少有一个磁盘节点,也就是在集群环境中需要添加 2 台及以上的磁盘节点,这样其中一台发生故障了,集群仍然可以保持运行。其它节点均设置为内存节点,这样会让队列和交换器声明之类的操作会更加快速,元数据同步也会更加高效。

三、集群部署

3.1 docker 安装

可以选3台虚拟机部署docker,也可以一台虚拟机上部署3个docker,原理都差不多,目的是布3套rabbitmq的环境,并能够互通
我们这为了方便演示,在同一台主机上部署3台虚拟机,保证他们互相之间可以连通,我们先创建一个 docker 网络

docker network create backend

创建完之后可以查看

docker network ls
docker run -d --hostname rabbit_host1 --name rabbitmq1 -p 15672:15672 -p 5672:5672 --network=backend -e RABBITMQ_ERLANG_COOKIE='rabbitmq_cookie' rabbitmq:management
docker run -d --hostname rabbit_host2 --name rabbitmq2 -p 5673:5672 --network=backend -e RABBITMQ_ERLANG_COOKIE='rabbitmq_cookie' rabbitmq:management
docker run -d --hostname rabbit_host3 --name rabbitmq3 -p 5674:5672 --network=backend -e RABBITMQ_ERLANG_COOKIE='rabbitmq_cookie' rabbitmq:management

Erlang Cookie 值必须相同,也就是一个集群内 RABBITMQ_ERLANG_COOKIE 参数的值必须相同。因为 RabbitMQ 是用Erlang实现的,Erlang Cookie 相当于不同节点之间通讯的密钥,Erlang节点通过交换 Erlang Cookie 获得认证。

3.2 将节点加入到集群里

第1台不动,另外两台分别执行下面命令

第2台

docker exec -it rabbitmq2 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq_host1
rabbitmqctl start_app
exit

第3台

docker exec -it rabbitmq3 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq_host1
rabbitmqctl start_app
exit

主要参数:

--ram 表示设置为内存节点,忽略次参数默认为磁盘节点。该配置启动了3个节点,1个磁盘节点和2个内存节点。

设置好之后,使用 http://ip:15672 进行访问,默认账号密码:guest/guest

WechatIMG824.png

可以看到,已经有多个节点了。

四 镜像队列

部署好集群以后,就是设置如何实时的同步队列和其中的数据,这边就会使用RabbitMQ的镜像功能

4.1 策略policy概念

使用RabbitMQ镜像功能,需要基于RabbitMQ策略来实现,策略policy是用来控制和修改群集范围的某个vhost队列行为和Exchange行为。策略policy就是要设置哪些Exchange或者queue的数据需要复制、同步,以及如何复制同步。

为了使队列成为镜像队列,需要创建一个策略来匹配队列,设置策略有两个键“ha-mode和 ha-params(可选)”。ha-params根据ha-mode设置不同的值,下表说明这些key的选项。

rabbit1.png

4.2 添加策略

登录rabbitmq管理页面 ——> Admin ——> Policies ——> Add / update a policy

rabbit2.png

name:随便取,策略名称
Pattern:^ 匹配符,只有一个^代表匹配所有
Definition:ha-mode=all 为匹配类型,分为3种模式:all(表示所有的queue)
或者使用命令:

rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

4.3 查看效果

此策略会同步所在同一VHost中的交换器和队列数据。设置好policy之后,使用 http://ip:15672 再次进行访问,可以看到队列镜像同步。

rabbit3.png

加餐,spring boot中配置 rabbitmq 集群

单机

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: username
    password: password

或者使用 address

spring:
  rabbitmq:
    addresses:ip1:port1
    username: username
    password: password

集群

addresses节点用逗号分隔

spring:
  rabbitmq:
    addresses:ip1:port1,ip2:port2,ip3:port3
    username: username
    password: password
文章目录