简介
本文介绍如何搭建 RabbitMQ 集群,使用 RabbitMQ-3.8.34 版本
相关博客:
- RabbitMQ集群搭建
环境规划
服务器清单如下,我在本地创建了如下三台虚拟机
| 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 映射,保证节点间可通过名称互相访问:
cat>>/etc/hosts<<EOF 192.168.249.137 node1 192.168.249.138 node2 192.168.249.139 node3 EOF配置完成示意:
(2)创建数据目录
为保证容器删除后数据不丢失,建议挂载数据卷:
# 在三台服务器分别执行mkdir-p/data/rabbitmq(3)创建 RabbitMQ 容器
重要说明:所有节点必须使用相同的 Erlang Cookie才能加入同一集群。
节点1(192.168.249.137)
连接 192.168.249.137 执行下面的命令:
dockerrun-d\--hostnamenode1\--namerabbitmq\--net=host\--restart=always\-v/data/rabbitmq:/var/lib/rabbitmq\-eRABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123"\-eRABBITMQ_DEFAULT_USER=admin\-eRABBITMQ_DEFAULT_PASS=admin\rabbitmq:3.8.34-management启动完成示意:
节点2(192.168.249.138)
连接 192.168.249.138 执行下面的命令:
dockerrun-d\--hostnamenode2\--namerabbitmq\--net=host\--restart=always\-v/data/rabbitmq:/var/lib/rabbitmq\-eRABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123"\-eRABBITMQ_DEFAULT_USER=admin\-eRABBITMQ_DEFAULT_PASS=admin\rabbitmq:3.8.34-management启动完成示意:
节点 3(192.168.249.139)
连接 192.168.249.139 执行下面的命令:
dockerrun-d\--hostnamenode3\--namerabbitmq\--net=host\--restart=always\-v/data/rabbitmq:/var/lib/rabbitmq\-eRABBITMQ_ERLANG_COOKIE="rabbitmq_cookie_123"\-eRABBITMQ_DEFAULT_USER=admin\-eRABBITMQ_DEFAULT_PASS=admin\rabbitmq:3.8.34-management启动完成示意:
(4)组建集群
以 node1 为主节点,将 node2 和 node3 加入到 node1 组成的集群中
连接 192.168.249.138 执行下面的命令,将节点 2 加入集群
# 停止节点应用dockerexecrabbitmq rabbitmqctl stop_app# 重置节点状态dockerexecrabbitmq rabbitmqctl reset# 加入到node1集群dockerexecrabbitmq rabbitmqctl join_cluster rabbit@node1# 重新启动应用dockerexecrabbitmq rabbitmqctl start_app加入完成示意:
连接 192.168.249.139 执行下面的命令,将节点3加入集群
dockerexecrabbitmq rabbitmqctl stop_appdockerexecrabbitmq rabbitmqctl resetdockerexecrabbitmq rabbitmqctl join_cluster rabbit@node1dockerexecrabbitmq rabbitmqctl start_app加入完成示意:
验证
(1) 查看集群状态
连接任意一个节点,执行以下命令查看集群状态:
dockerexecrabbitmq 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)故障转移测试
# 1. 停掉一个节点dockerstop rabbitmq# 2. 在其他节点查看集群状态,确认队列仍可用dockerexecrabbitmq rabbitmqctl cluster_status# 3. 重启节点,观察是否自动重新加入集群dockerstart rabbitmq项目整合
(1) 配置文件修改
原单机配置:
spring:rabbitmq:host:${rabbitmq.ip}port:${rabbitmq.port}username:${rabbitmq.username}password:${rabbitmq.password}template:reply-timeout:30000listener:simple:missing-queues-fatal:false修改为集群配置,配置所有节点地址:
spring:rabbitmq:addresses:192.168.249.137:5672,192.168.249.138:5672,192.168.249.139:5672username:${rabbitmq.username}password:${rabbitmq.password}template:reply-timeout:30000listener:simple:missing-queues-fatal:false配置建议:将全部 3 个节点都写入配置,作为集群入口。只配置少数节点时,若这些入口节点同时宕机,即使集群本身仍可用,应用也无法连接。
(2)Java 代码修改
a. ConnectionFactory Bean 初始化
如果你的项目中,使用了手动初始化 RabbitMQ 的连接工厂,需要做以下修改
原代码:
@Value("${rabbitmq.ip}")privateStringhost;@Value("${rabbitmq.port}")privateintport;@Value("${rabbitmq.username}")privateStringusername;@Value("${rabbitmq.password}")privateStringpassword;@Value("${rabbitmq.cache_size}")privateintcacheSize;@BeanpublicConnectionFactoryrabbitMQConnectionFactory(){CachingConnectionFactoryconnectionFactory=newCachingConnectionFactory();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);returnconnectionFactory;}修改后,改用集群地址配置:
@Value("${spring.rabbitmq.addresses}")privateStringaddresses;@Value("${spring.rabbitmq.username}")privateStringusername;@Value("${spring.rabbitmq.password}")privateStringpassword;@Value("${rabbitmq.cache_size}")privateintcacheSize;@BeanpublicConnectionFactoryrabbitMQConnectionFactory(){CachingConnectionFactoryconnectionFactory=newCachingConnectionFactory();connectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(10240);connectionFactory.setAddresses(addresses);// 使用 addresses 替代 hostconnectionFactory.setUsername(username);connectionFactory.setPassword(password);connectionFactory.setChannelCacheSize(cacheSize);connectionFactory.setChannelCheckoutTimeout(2000);connectionFactory.getRabbitConnectionFactory().setAutomaticRecoveryEnabled(true);connectionFactory.getRabbitConnectionFactory().setNetworkRecoveryInterval(10000);connectionFactory.getRabbitConnectionFactory().setTopologyRecoveryEnabled(true);returnconnectionFactory;}b. 创建队列(非注解方式)- 使用 Quorum 队列
原代码:
@BeanpublicQueuetestQueue(){returnnewQueue("testQueue",true);}修改为Quorum队列,保证高可用:
@BeanpublicQueuetestQueue(){returnQueueBuilder.durable("testQueue").quorum()// 声明为Quorum队列.build();}c. 创建队列(注解方式)- 使用 Quorum 队列
原代码:
@RabbitListener(bindings=@QueueBinding(value=@Queue(value="testQueue",autoDelete="false"),exchange=@Exchange(value="testExchange",type=ExchangeTypes.TOPIC)),containerFactory="rabbitMQConnectionFactory")修改后,增加x-queue-type参数声明为 Quorum 队列:
@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 密码
监控告警:建议监控队列堆积、节点状态、连接数等指标