简介
本文介绍如何搭建 RabbitMQ 集群,使用 RabbitMQ-3.8.34 版本
相关博客:
环境规划
服务器清单如下,我在本地创建了如下三台虚拟机
| IP 地址 | 节点名称 | 说明 |
|---|---|---|
| 192.168.249.137 | node1 | 主节点,集群入口 |
| 192.168.249.138 | node2 | 从节点 |
| 192.168.249.139 | node3 | 从节点 |
服务器环境示意:

集群架构
- 共 3 个 RabbitMQ 节点,采用镜像队列 / Quorum 队列实现高可用
- 采用奇数节点数( 3 节点)避免脑裂问题
- Quorum 队列基于 Raft 算法,至少需要 2 个节点在线才能正常工作
环境准备
Docker 环境
- 所有服务器需提前安装 Docker 环境
- 确认 Docker 服务正常运行:
systemctl status docker
端口开放说明
RabbitMQ 集群需要开放以下端口:
4369:epmd,节点发现服务5672:AMQP 协议,客户端连接端口15672:管理界面 Web 端口25672:节点间通信端口
搭建
(1)添加节点信息
在三台服务器上执行以下命令,添加节点 hosts 映射,保证节点间可通过名称互相访问:
shell
cat >> /etc/hosts << EOF
192.168.249.137 node1
192.168.249.138 node2
192.168.249.139 node3
EOF
配置完成示意:

(2)创建数据目录
为保证容器删除后数据不丢失,建议挂载数据卷:
shell
# 在三台服务器分别执行
mkdir -p /data/rabbitmq
(3)创建 RabbitMQ 容器
重要说明 :所有节点必须使用相同的 Erlang Cookie 才能加入同一集群。
节点1(192.168.249.137)
连接 192.168.249.137 执行下面的命令:
shell
docker run -d \
--hostname node1 \
--name rabbitmq \
--net=host \
--restart=always \
-v /data/rabbitmq:/var/lib/rabbitmq \
-e RABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123" \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
rabbitmq:3.8.34-management
启动完成示意:

节点2(192.168.249.138)
连接 192.168.249.138 执行下面的命令:
shell
docker run -d \
--hostname node2 \
--name rabbitmq \
--net=host \
--restart=always \
-v /data/rabbitmq:/var/lib/rabbitmq \
-e RABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123" \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
rabbitmq:3.8.34-management
启动完成示意:

节点 3(192.168.249.139)
连接 192.168.249.139 执行下面的命令:
shell
docker run -d \
--hostname node3 \
--name rabbitmq \
--net=host \
--restart=always \
-v /data/rabbitmq:/var/lib/rabbitmq \
-e RABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123" \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
rabbitmq:3.8.34-management
启动完成示意:

(4)组建集群
以 node1 为主节点,将 node2 和 node3 加入到 node1 组成的集群中
连接 192.168.249.138 执行下面的命令,将节点 2 加入集群
shell
# 停止节点应用
docker exec rabbitmq rabbitmqctl stop_app
# 重置节点状态
docker exec rabbitmq rabbitmqctl reset
# 加入到node1集群
docker exec rabbitmq rabbitmqctl join_cluster rabbit@node1
# 重新启动应用
docker exec rabbitmq rabbitmqctl start_app
加入完成示意:

连接 192.168.249.139 执行下面的命令,将节点3加入集群
shell
docker exec rabbitmq rabbitmqctl stop_app
docker exec rabbitmq rabbitmqctl reset
docker exec rabbitmq rabbitmqctl join_cluster rabbit@node1
docker exec rabbitmq rabbitmqctl start_app
加入完成示意:

验证
(1) 查看集群状态
连接任意一个节点,执行以下命令查看集群状态:
shell
docker exec rabbitmq rabbitmqctl cluster_status
成功标志:三个节点都显示在 running_nodes 列表中
集群状态示意:

(2)管理控制台验证
通过浏览器访问任意节点的管理后台,使用账号密码 admin/admin 登录:
- node1: http://192.168.249.137:15672
- node2: http://192.168.249.138:15672
- node3: http://192.168.249.139:15672
登录后在 "Nodes" 页面可以看到所有三个节点状态。
控制台示意:

(3) 高可用验证(Quorum 队列)
RabbitMQ 3.8+ 推荐使用 Quorum 队列 替代传统镜像队列:
| 特性 | 普通队列 | 镜像队列 | Quorum 队列 |
|---|---|---|---|
| 高可用 | ❌ 单点 | ✅ 主从复制 | ✅ Raft算法 |
| 数据一致性 | - | 最终一致 | 强一致 |
| 脑裂问题 | - | 可能发生 | 不会发生 |
| 最小节点数 | 1 | 2 | 3(建议奇数) |
(4)故障转移测试
shell
# 1. 停掉一个节点
docker stop rabbitmq
# 2. 在其他节点查看集群状态,确认队列仍可用
docker exec rabbitmq rabbitmqctl cluster_status
# 3. 重启节点,观察是否自动重新加入集群
docker start rabbitmq
项目整合
(1) 配置文件修改
原单机配置:
yml
spring:
rabbitmq:
host: ${rabbitmq.ip}
port: ${rabbitmq.port}
username: ${rabbitmq.username}
password: ${rabbitmq.password}
template:
reply-timeout: 30000
listener:
simple:
missing-queues-fatal: false
修改为集群配置,配置所有节点地址:
yml
spring:
rabbitmq:
addresses: 192.168.249.137:5672,192.168.249.138:5672,192.168.249.139:5672
username: ${rabbitmq.username}
password: ${rabbitmq.password}
template:
reply-timeout: 30000
listener:
simple:
missing-queues-fatal: false
配置建议:将全部 3 个节点都写入配置,作为集群入口。只配置少数节点时,若这些入口节点同时宕机,即使集群本身仍可用,应用也无法连接。
(2)Java 代码修改
a. ConnectionFactory Bean 初始化
如果你的项目中,使用了手动初始化 RabbitMQ 的连接工厂,需要做以下修改
原代码:
java
@Value("${rabbitmq.ip}")
private String host;
@Value("${rabbitmq.port}")
private int port;
@Value("${rabbitmq.username}")
private String username;
@Value("${rabbitmq.password}")
private String password;
@Value("${rabbitmq.cache_size}")
private int cacheSize;
@Bean
public ConnectionFactory rabbitMQConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(10240);
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setChannelCacheSize(cacheSize);
connectionFactory.setChannelCheckoutTimeout(2000);
connectionFactory.getRabbitConnectionFactory().setAutomaticRecoveryEnabled(true);
connectionFactory.getRabbitConnectionFactory().setNetworkRecoveryInterval(10000);
connectionFactory.getRabbitConnectionFactory().setTopologyRecoveryEnabled(true);
return connectionFactory;
}
修改后,改用集群地址配置:
java
@Value("${spring.rabbitmq.addresses}")
private String addresses;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${rabbitmq.cache_size}")
private int cacheSize;
@Bean
public ConnectionFactory rabbitMQConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(10240);
connectionFactory.setAddresses(addresses); // 使用 addresses 替代 host
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setChannelCacheSize(cacheSize);
connectionFactory.setChannelCheckoutTimeout(2000);
connectionFactory.getRabbitConnectionFactory().setAutomaticRecoveryEnabled(true);
connectionFactory.getRabbitConnectionFactory().setNetworkRecoveryInterval(10000);
connectionFactory.getRabbitConnectionFactory().setTopologyRecoveryEnabled(true);
return connectionFactory;
}
b. 创建队列(非注解方式)- 使用 Quorum 队列
原代码:
java
@Bean
public Queue testQueue() {
return new Queue("testQueue", true);
}
修改为Quorum队列,保证高可用:
java
@Bean
public Queue testQueue() {
return QueueBuilder.durable("testQueue")
.quorum() // 声明为Quorum队列
.build();
}
c. 创建队列(注解方式)- 使用 Quorum 队列
原代码:
java
@RabbitListener(bindings =@QueueBinding(
value = @Queue(value = "testQueue", autoDelete = "false"),
exchange = @Exchange(value = "testExchange", type = ExchangeTypes.TOPIC)
),containerFactory = "rabbitMQConnectionFactory")
修改后,增加 x-queue-type 参数声明为 Quorum 队列:
java
@RabbitListener(bindings =@QueueBinding(
value = @Queue(value = "testQueue", autoDelete = "false",
arguments = @Argument(name = "x-queue-type", value = "quorum")),
exchange = @Exchange(value = "testExchange", type = ExchangeTypes.TOPIC)
),containerFactory = "rabbitMQConnectionFactory")
注意事项
-
Erlang Cookie一致:所有节点的 Cookie 必须完全相同,否则无法加入集群
-
主机名解析:确保所有节点能通过 hostname 互相访问,/etc/hosts配置必须一致
-
Quorum 队列节点数 :Quorum 队列基于 Raft 算法,至少需要 (N/2)+1 个节点在线才能写入。3 节点集群最多允许 1 个节点宕机
-
数据持久化:生产环境务必挂载数据卷,防止容器删除导致数据丢失
-
防火墙配置:开放 4369、5672、15672、25672 四个端口
-
密码安全:生产环境建议修改默认的 admin/admin 密码
-
监控告警:建议监控队列堆积、节点状态、连接数等指标