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. 监控告警:建议监控队列堆积、节点状态、连接数等指标

相关推荐
heimeiyingwang1 天前
【架构实战】分布式ID生成方案:雪花算法与业务ID设计
分布式·算法·架构
AOwhisky1 天前
Ceph系列第一期:Ceph分布式存储核心概念与架构初识
linux·运维·笔记·分布式·ceph·学习·架构
Plastic garden1 天前
Kafka
分布式·kafka
未若君雅裁1 天前
Kafka 顺序消费:分区、消费者组、Key与业务有序性
分布式·微服务·kafka
AOwhisky1 天前
Ceph系列第二期:Ceph集群部署实战(cephadm)
linux·运维·笔记·分布式·ceph·云计算·存储
qiuyepiaoling1 天前
rabbitmq 基础
分布式·rabbitmq·ruby
未若君雅裁1 天前
Kafka 消息可靠性:发送确认、acks、副本保存与Offset手动提交
分布式·微服务·kafka
phltxy1 天前
RabbitMQ 事务与消息分发
分布式·rabbitmq
better_liang1 天前
每日Java面试场景题知识点之-分布式秒杀系统的设计
java·redis·分布式·消息队列·高并发·秒杀系统·限流降级
woniu_buhui_fei1 天前
分布式限流
java·分布式