持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
集群模式
在RabbitMQ
中,一个节点的服务其实也是作为一个集群来处理的,在web
控制台的admin-> cluster
中可以看到集群的名字,并且可以在页面上修改。而多节点的集群有两种模式。
默认的普通集群模式:
这种模式使用Erlang
语言天生具备的集群方式搭建。这种集群模式下,集群的各个节点之间只会有相同的元数据,即队列结构,而消息不会进行冗余,只存在一个节点中。消费时,如果消费的不是存有数据的节点,RabbitMQ
会临时在节点之间进行数据传输,将消息从存有数据的节点传输到消费的节点。
很显然,这种集群模式的消息可靠性不是很高。因为如果其中有个节点服务宕机了,那这个节点上的数据就无法消费了,需要等到这个节点服务恢复后才能消费,而这时,消费者端已经消费过的消息就有可能给不了服务端正确应答,服务起来后,就会再次消费这些消息,造成这部分消息重复消费。 另外,如果消息没有做持久化,重启就消息就会丢失。
这种集群模式也不支持高可用,即当某一个节点服务挂了后,需要手动重启服务,才能保证这一部分消息能正常消费。
这种集群模式只适合一些对消息安全性不是很高的场景。而在使用这种模式时,消费者应该尽量的连接上每一个节点,减少消息在集群中的传输。
镜像模式:
这种模式是在普通集群模式基础上的一种增强方案,这也就是RabbitMQ
的官方HA
高可用方案。需要在搭建了普通集群之后再补充搭建。其本质区别在于,这种模式会在镜像节点中间主动进行消息同步,而不是在客户端拉取消息时临时同步。
在集群内部有一个算法会选举产生master
和slave
,当一个master
挂了后,也会自动选出一个来。从而给整个集群提供高可用能力。
这种模式的消息可靠性更高,因为每个节点上都存着全量的消息。而他的弊端也是明显的,集群内部的网络带宽会被这种同步通讯大量的消耗,进而降低整个集群的性能。这种模式下,队列数量最好不要过多。
集群的注意事项
节点名的重要性
在集群中,节点名必须要唯一,集群中是通过节点名来进行联系的。
节点名组成:rabbit@
+ 主机名
erlang cookie 的重要性
不同的操作系统这个文件的位置是不一样的。
Linux
系统这个文件的位置一般是在 /var/lib/rabbitmq/.erlang.cookie
。
.erlang.cookie
的文件权限一般给600
就可以了,不要给太高或太低,否则集群可能无法启动。
集群中加入节点
新加入的集群的节点必须是一个全新的节点,不可以带有数据,如果存在则需要在加入集群的节点上执行rabbitmqctl reset
。
环境准备
3台centos7
操作系统,分别配置机器名为 centos1
、centos2
和centos3
,预装rabbitmq
,然后关闭防火墙(或者找到RabbitMQ
的业务端口全部打开,amqp
端口5672
,http Api
端口15672
,集群通信端口25672
搭建普通集群
1. 配置主机名和IP
地址映射
sudo vim /etc/hosts
# 配置IP和主机名映射 IP 主机名,修改完成保存
192.168.124.81 centos1
192.168.124.82 centos2
192.168.124.84 centos3
2. 配置主机名与IP同步集群节点中的cookie
默认会在/var/lib/rabbitmq/
目录下生成一个.erlang.cookie
。里面有一个字符串。我们要做的就是保证集群中三个节点的这个cookie
字符串一致。
将centos2
机器做为主节点,将另外2台机器加入到centos2
的RabbitMQ
集群中,所以将centos2
的.erlang.cookie
文件分发到centos1
和centos3
中或将文件改成一致的。
sudo scp /var/lib/rabbitmq/.erlang.cookie root@centos1:/var/lib/rabbitmq/.erlang.cookie
sudo scp /var/lib/rabbitmq/.erlang.cookie root@centos3:/var/lib/rabbitmq/.erlang.cookie
3. 将centos1
和centos3
加入到centos2
中
- 保证
centos2
上的rabbitmq
服务是正常启动的
sudo rabbitmq-server -detached
- 在
centos1
和centos3
分别执行以下指令
#放行端口
sudo firewall-cmd --zone=public --add-port=5672/tcp --add-port=15672/tcp --add-port=4369/tcp --add-port=25672/tcp --permanent
sudo firewall-cmd --reload
rabbitmq-server -detached
# 先停止当前服务器上rabbitmq服务
sudo rabbitmqctl stop_app
# 重置
sudo rabbitmqctl reset
# 加入到centos2 节点 ,
sudo rabbitmqctl join_cluster --disc rabbit@centos2
# 启动rabbitmq服务
sudo rabbitmqctl start_app
--disc
表示以disc
节点加入集群,--ram
则表示以ram
节点加入集群。RabbitMQ
的集群节点这两种:
disc
节点会将元数据保存到硬盘中ram
节点会将元数据保存在内存中
由于ram
节点减少了很多与硬盘的交互,所以,ram
节点的元数据使用性能会比较高。但是,同时,这也意味着元数据的安全性是不如disc
节点的,因此,是存在单点故障的。如果centos2
节点服务崩溃,那么元数据就有可能丢失。在企业进行部署时,性能与安全性需要自己进行平衡。
这里说的元数据仅仅只包含交换机、队列等的定义,而不包含具体的消息。因此,ram
节点的性能提升,仅仅体现在对元数据进行管理时,比如修改队列queue
,交换机exchange
,虚拟机vhosts
等时,与消息的生产和消费速度无关。
如果一个集群中,全部都是ram
节点,那么元数据就有可能丢失。这会造成集群停止之后就启动不起来了。RabbitMQ
会尽量阻止创建一个全是ram节点的集群,但是并不能彻底阻止。所以,综合考虑,官方其实并不建议使用ram
节点,更推荐保证集群中节点的资源投入,使用disc
节点。
- 进入
centos2
的web
管理界面查看节点情况
也可以用后台指令查看集群状态 sudo rabbitmqctl cluster_status
4.从集群移除centos1
- 将需要被移除的节点停止
sudo rabbitmqctl stop
- 在其他运行节点执行
sudo rabbitmqctl forget_cluster_node rabbit@centos1
- 重新加入集群
sudo rabbitmq-server -detached
sudo rabbitmqctl stop_app
sudo rabbitmqctl reset
sudo rabbitmqctl join_cluster rabbit@centos2 --ram
sudo rabbitmqctl start_app
在启动的时候可能报如下错误Node rabbit@centos1 thinks it's clustered with node rabbit@centos2, but rabbit@centos2 disagrees
,这个时候我们需要删除rm -rvf /var/lib/rabbitmq/mnesia/
这个目录,然后在次执行加入集群的命令即可。
5. 踩坑
每个节点都需要安装rabbitmq_management
,否则在界面上没有安装的节点会出现问题
搭建镜像集群
我们在普通集群的基础上面就可以完成镜像集群的搭建
通常在生产环境中,为了减少RabbitMQ
集群之间的数据传输,在配置镜像策略时,会针对固定的虚拟主机virtual host
来配置。
RabbitMQ
中的vritual host
可以类比为MySQL
中的库,针对每个虚拟主机,可以配置不同的权限、策略等。并且不同虚拟主机之间的数据是相互隔离的。
我们首先创建一个/mirror
的虚拟主机,然后再添加给对应的镜像策略:
# 在任意一个节点执行
# 创建虚拟主机,重复执行不会创建多个
sudo rabbitmqctl add_vhost /mirror
# 添加镜像策略
sudo rabbitmqctl set_policy ha-all --vhost "/mirror" "^" '{"ha-mode":"all"}'
同样,这些配置的策略也可以在Web
控制台操作。另外也提供了HTTP API
来进行这些操作。
其中,pattern
是队列的匹配规则, ^
表示全部匹配。 ^ ha
这样的配置表示以ha
开头。通常就用虚拟主机来区分就够了,这个队列匹配规则就配置成全匹配。
然后几个关键的参数:
HA mode
: 可选值 all
, exactly
, nodes
。生产上通常为了保证高可用,就配all
all
: 队列镜像到集群中的所有节点。当新节点加入集群时,队列也会被镜像到这个节点。exactly
: 需要搭配一个数字类型的参数(ha-params
)。队列镜像到集群中指定数量的节点。如果集群内节点数少于这个数字,则队列镜像到集群内的所有节点。如果集群内节点少于这个数,当一个包含镜像的节点停止服务后,新的镜像就不会去另外找节点进行镜像备份了。nodes
: 需要搭配一个字符串类型的参数。将队列镜像到指定的节点上。如果指定的队列不在集群中,不会报错。当声明队列时,如果指定的所有镜像节点都不在线,那队列会被创建在发起声明的客户端节点上。
通常镜像模式的集群已经足够满足大部分的生产场景了。虽然他对系统资源消耗比较高,但是在生产环境中,系统的资源都是会做预留的,所以正常的使用是没有问题的。但是在做业务集成时,还是需要注意队列数量不宜过多,并且尽量不要让RabbitMQ
产生大量的消息堆积。
这样搭建起来的RabbitMQ
已经具备了集群特性,往任何一个节点上发送消息,消息都会及时同步到各个节点中。而在实际企业部署时,往往会以RabbitMQ
的镜像队列作为基础,再增加一些运维手段,进一步提高集群的安全性和实用性。
例如,增加keepalived
保证每个RabbitMQ
的稳定性,当某一个节点上的RabbitMQ
服务崩溃时,可以及时重新启动起来。另外,也可以增加HA-proxy
来做前端的负载均衡,通过HA-proxy
增加一个前端转发的虚拟节点,应用可以像使用一个单点服务一样使用一个RabbitMQ
集群。
常用命令
集群管理
#查看集群状态
rabbitmqctl cluster_status
#设置集群名称
rabbitmqctl set_cluster_name {clusterName}
#重命名节点名称
rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2 newnode2] [oldnode3 newnode3...]
#删除指定节点
rabbitmqctl forget_cluster_node {nodeName} [--offline]
# 指示已加入集群的节点上线后联系clusternode,这不同于join_cluster,因为它不加入任何集群,
# 它会检查clusternode已经以集群的形式存在于集群中了
# 需要这个命令的动机是当节点离线时,集群可以变化
rabbitmqctl update_cluster_nodes
#加入集群
rabbitmqctl join_cluster [--disc|--arm] {nodeName}
# 更改集群节点类型
rabbitmqctl change_cluster_node_type [--disk|--ram]
清空状态
# 表示设置RabbitMQ节点为原始状态。会从该节点所属的cluster中都删除,从管理数据库中删除所有数据,例如配置的用户和vhost,还会删除所有的持久消息。
# 要想reset和force_reset操作执行成功,RabbitMQ应用需要处于停止状态,即执行过 rabbitmqctl stop_app
rabbitmqctl reset
# 表示强制性地设置RabbitMQ节点为原始状态。它和reset的区别在于,可以忽略目前管理数据库的状态和cluster的配置,无条件的reset。
# 虽然会忽略目前管理的mnesia数据库和cluster配置,但是会删除掉当前节点的mnesia数据库配置和cluster配置,集群中的其它节点需要使用rabbitmqctl forget_cluster_node rabbit@hostname
# 该方法的使用,应当用在当数据库或者cluster配置损坏的情况下作为最后的方法。
rabbitmqctl force_reset