单机版的RabbitMQ服务器集群模式,不仅要面临服务器因为各种原因导致崩溃无法使用的情况,其吞吐量也是有限的。购买昂贵的服务器来增强单机RabbitMQ服务的性能显得捉襟见肘时,搭建一个RabbitMQ集群才是解决实际问题的关键。
RabbitMQ集群允许消费者和生产者在RabbitMQ单个节点崩溃的情况下继续运行,它可以通过添加更多的节点来线性地扩展消息通信的吞吐量。当其中一个RabbitMQ故障时,客户端能够重新连接到集群中的任何其它节点并继续生产或者消费。
不过RabbitMQ集群不能保证消息的万无一失,即将消息、队列、交换器等都设置为可持久化,生产者和消费者都正确地使用确认方式。当集群中一个RabbitMQ节点崩溃时,该节点上的所有队列中的消息也会丢失。RabbitMQ集群中的所有节点都会备份所有的元数据信息,包括以下内容:
- 队列元数据:队列的名称及其属性。
- 交换器:交换器的名称及其属性。
- 绑定关系元数据:交换器与队列或者交换器与交换器之间的绑定关系。
- vhost元数据:为vhost内的队列、交换器和绑定提供命名空间及完全属性。
但是不会备份消息,基于存储空间和性能考虑,在RabbitMQ集群中创建队列,集群只会在单个节点而不是所有节点上创建队列的进程并包含完整的队列信息(元数据、状态、内容)。这样只有队列的宿主节点,即所有者节点知道队列的所有信息,其他节点只知道队列的元数据和指向该队列存在的那个节点的指针。因此当集群节点崩溃时,该节点的队列进程和关联的绑定都会消息。附件在那些队列上的消费者也会丢失其所订阅的信息,并且任何匹配该队列绑定信息的新消息也会丢失。
不同队列那样拥有自己的进程,交换器其实只是一个名词和绑定列表。当消息发布到交换器时,实际上是有所连接的信道将消息上的路由键同交换器绑定列表进行比较,然后再路由消息。当创建一个新的交换器时,RabbitMQ要做的就是将绑定列表添加到集群中所有节点上。这样,每个节点上的每条信道都可以访问到新的交换器了。
1. 单机多节点配置
在一台机器上部署多个RabbitMQ服务节点,需要确保每个节点都有独立的名称、数据存储位置、端口号(包括插件的端口号)等。现在我们在主机名称为localhost的机器上创建一个由rabbit1@localhost
、rabbit2@localhost
和rabbit3@localhost
这三个节点组成的RabbitMQ集群。
1.1 依次启动各个节点
首先需要确保机器上已经安装了Erlang
和RabbitMQ
,其次为每个RabbitMQ服务节点设置不同的端口号和节点名称来启动相应的服务。
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit1 rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit2 rabbitmq-server -detached
RABBITMQ_NODE_PORT=5674 RABBITMQ_NODENAME=rabbit3 rabbitmq-server -detached
若rabbit2和rabbit3启动失败,需要检查是否启用了RabbitMQ Management插件。如果开启了,就需要为每个服务节点配置一个对应插件的端口号,具体如下:
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit1 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15672}]" rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit2 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" rabbitmq-server -detached
RABBITMQ_NODE_PORT=5674 RABBITMQ_NODENAME=rabbit3 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15674}]" rabbitmq-server -detached
每个节点启动之后,我们可以使用rabbitmqctl -n rabbitx@localhost status
查看每个节点的状态。
1.2 搭建集群
(1)将rabbit2@localhost
节点加入rabbit1@localhost
的集群之中
# 1.先停用rabbit2@localhost的应用
rabbitmqctl -n rabbit2@localhost stop_app
# 2.重置rabbit2@localhost节点
rabbitmqctl -n rabbit2@localhost reset
# 3.加入rabbit1@localhost集群
rabbitmqctl -n rabbit2@localhost join_cluster rabbit1@localhost
# 4.启动rabbit2@localhost应用
rabbitmqctl -n rabbit2@localhost start_app
(2)将rabbit3@localhost
节点加入集群之中
# 1.先停用rabbit3@localhost的应用
rabbitmqctl -n rabbit3@localhost stop_app
# 2.重置rabbit3@localhost节点
rabbitmqctl -n rabbit3@localhost reset
# 3.加入rabbit1@localhost集群
rabbitmqctl -n rabbit3@localhost join_cluster rabbit1@localhost
# 4.启动rabbit3@localhost应用
rabbitmqctl -n rabbit3@localhost start_app
(3)至此集群搭建成功,查看集群状态
rabbitmqctl -n rabbit1@localhost cluster_status
2. 多机多节点配置
先来看一下服务器列表:
机器ip | 主机名 |
---|---|
192.168.1.19 | node1 |
192.168.1.20 | node2 |
192.168.1.21 | node3 |
2.1 修改host配置
使用vim命令编辑/etc/hosts
文件,添加以下配置:
192.168.1.19 node1
192.168.1.20 node2
192.168.1.21 node3
2.2 安装RabbitMQ
参考《RabbitMQ:安装》在每台机器上搭建RabbitMQ服务。
2.3 同步cookie文件
编辑RabbitMQ的cookie文件,以确保各个节点的cookie文件使用的是同一个值。可以读取node1节点的cookie值,然后将其复制到node2和node3节点中。
cookie文件默认路径为/var/lib/rabbitmq/.erlang.cookie
或者$HOME/.erlang.cookie
。cookie相当于密钥令牌,集群中的RabbitMQ节点需要通过交换密钥令牌以获得相互认证。如果节点的密钥令牌不一致,那么在配置节点时就会有如下的错误:
Clustering node rabbit@node2 with rabbit@node1
Error: unable to perform an operation on node 'rabbit@node1'. Please see diagnostics information and suggestions below.
Most common reasons for this are:
* Target node is unreachable (e.g. due to hostname resolution, TCP connection or firewall issues)
* CLI tool fails to authenticate with the server (e.g. due to CLI tool's Erlang cookie not matching that of the server)
* Target node is not running
In addition to the diagnostics info below:
* See the CLI, clustering and networking guides on https://rabbitmq.com/documentation.html to learn more
* Consult server logs on node rabbit@node1
* If target node is configured to use long node names, don't forget to use --longnames with CLI tools
DIAGNOSTICS
===========
attempted to contact: [rabbit@node1]
rabbit@node1:
* connected to epmd (port 4369) on node1
* epmd reports node 'rabbit' uses port 25672 for inter-node and CLI tool traffic
* TCP connection succeeded but Erlang distribution failed
######## 提示检查cookie文件
* Authentication failed (rejected by the remote node), please check the Erlang cookie
Current node details:
* node name: 'rabbitmqcli-90630-rabbit@node2'
* effective user's home directory: /root
* Erlang cookie hash: 3Bun7gc7JbDUMKwVC2W2uQ==
2.4 配置集群
(1)首先启动node1、node2和node3这3个节点的RabbitMQ服务。
rabbitmq-server -detached
(2)目前这3个节点都是以独立节点存在的单个集群,可通过rabbitmqctl cluster_status
命令查看各个节点的状态。
(3)以node1节点为基准,将node2和node3节点加入到node1节点的集群中。
- 首先将node2加入node1集群中
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app
- 再将node3加入node1集群中
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app
至此集群搭建成功。我们可以在node1节点使用rabbitmqctl cluster_status
命令查看集群状态。
Cluster status of node rabbit@node1 ...
Basics
# 集群名称
Cluster name: rabbit@node1
# 磁盘节点
Disk Nodes
rabbit@node1
rabbit@node2
rabbit@node3
# 正在运行的节点
Running Nodes
rabbit@node1
rabbit@node2
rabbit@node3
# 版本
Versions
rabbit@node1: RabbitMQ 3.8.3 on Erlang 22.1
rabbit@node2: RabbitMQ 3.8.3 on Erlang 22.1
rabbit@node3: RabbitMQ 3.8.3 on Erlang 22.1
Alarms
(none)
# 网络分区
Network Partitions
(none)
# 端口
Listeners
Node: rabbit@node1, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@node1, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@node2, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@node2, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@node3, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@node3, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Feature flags
Flag: implicit_default_bindings, state: enabled
Flag: quorum_queue, state: enabled
Flag: virtual_host_metadata, state: enabled
3. 集群节点类型
在上面我们通过rabbitmqctl cluster_status
命令查看集群状态时,看到输出内容中有这样的一段:
Disk Nodes
rabbit@node1
rabbit@node2
rabbit@node3
这表明目前集群中的三个节点都是磁盘节点。RabbitMQ中的每一个节点,不管是单一节点系统或者是集群中的一部分,要么是内存节点,要么是磁盘节点。
内存节点将所有的队列、交换器、绑定关系、用户、权限和vhost的元数据定义都存储在内存中。而磁盘节点则将这些信息都存储到磁盘中。单节点的集群中必然只有磁盘类型的节点,否则当重启RabbitMQ之后,所有关于系统的配置信息都会丢失。不过在集群中,可以选择配置部分节点为内存节点,这样可以获得更高的性能。
示例,我们将node3节点由磁盘节点改成内存节点。针对node3是否已经处于集群中,分以下两种方式:
- node3已经在集群中,使用以下命令修改节点类型
# 1.先停止rabbitmq应用
rabbitmqctl stop_app
# 2.修改节点类型
rabbitmqctl change_cluster_node_type ram
# 3.启动rabbitmq应用
rabbitmqctl start_app
- node3还未加入集群,指定节点类型
rabbitmqctl join_cluster rabbit@node1 --ram
rabbitmqctl join_cluster rabbit@node1 --disc指定为磁盘类型
RabbitMQ只要求在集群中至少有一个磁盘节点,所有其他节点可以是内存节点。当节点加入或者离开集群时,它们必须将变更通知到至少一个磁盘节点。如果只有一个磁盘节点,而且该节点刚好挂掉了,那么集群可以继续发送或者接收信息,但是不能执行创建队列、交换器、绑定关系、用户,以及更改权限、添加或删除集群节点的操作了。所以通常建议集群中至少有2台节点是磁盘节点,或者为了确保集群信息的可靠性,又或者不确定使用磁盘节点还是内存节点的时候,建议全部使用磁盘节点。