RabbitMQ集群搭建

简介

本文介绍如何搭建 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 登录:

登录后在 "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")

注意事项

  1. Erlang Cookie一致:所有节点的 Cookie 必须完全相同,否则无法加入集群

  2. 主机名解析:确保所有节点能通过 hostname 互相访问,/etc/hosts配置必须一致

  3. Quorum 队列节点数 :Quorum 队列基于 Raft 算法,至少需要 (N/2)+1 个节点在线才能写入。3 节点集群最多允许 1 个节点宕机

  4. 数据持久化:生产环境务必挂载数据卷,防止容器删除导致数据丢失

  5. 防火墙配置:开放 4369、5672、15672、25672 四个端口

  6. 密码安全:生产环境建议修改默认的 admin/admin 密码

  7. 监控告警:建议监控队列堆积、节点状态、连接数等指标

相关推荐
国科安芯5 小时前
ASL706S:让 MCU 不再“失忆跑飞“的守护芯片
分布式·单片机·嵌入式硬件·安全·fpga开发·架构
阿无,7 小时前
RabbitMQ面试题
分布式·rabbitmq
盛世宏博智慧档案9 小时前
全国100个分布式机房环境温湿度智能监测系统建设方案
分布式·机房·温湿度
六bring个六10 小时前
分布式设备连接对端失败问题分析
分布式·分布式软总线
天涯明月199316 小时前
ray深度研究报告
大数据·人工智能·分布式·ray
Rain的Java大神之路1 天前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
大菠萝爱上小西瓜2 天前
【大数据实战】Spark 2.2.2 完全分布式集群搭建
大数据·分布式·spark
肠畔码农2 天前
深度解析 RocketMQ 消费端限流与重平衡(Rebalance):分布式队列分配与流控防雪崩本质
分布式·rocketmq
海兰2 天前
【 Kafka进阶3】Apache Kafka 分布式事件流平台:架构原理与微服务解耦机制简要分析
分布式·架构·kafka
Rain的Java大神之路2 天前
如何避免订单重复提交
java·redis·后端·面试·架构·rabbitmq·rocketmq