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

相关推荐
薪火铺子4 小时前
Redis 分布式锁与 Redisson 原理深度解析
java·redis·分布式·后端
skilllite作者5 小时前
Deer-Flow 工作流引擎深度评测报告
java·大数据·开发语言·chrome·分布式·架构·rust
摇滚侠5 小时前
Java 项目教程《黑马商城》微服务拆分 20 - 22
java·分布式·架构
乐之者v6 小时前
Kafka 跨服数据同步
分布式·kafka
喜欢流萤吖~6 小时前
分布式搜索引擎:Elasticsearch 从入门到实战
分布式·elasticsearch·搜索引擎
PawSQL6 小时前
同一条SQL,单机秒回,分布式集群卡成PPT——问题究竟出在哪?
数据库·分布式·sql
富士康质检员张全蛋7 小时前
Kafka 消息查找流程和消息读取流程
分布式·kafka
未来龙皇小蓝8 小时前
SpringBoot API日志系统设计-02:线程池异步化与RabbitMQ解耦
数据库·spring boot·后端·性能优化·rabbitmq·java-rabbitmq
深蓝轨迹8 小时前
Kafka入门教程--帮你理清所有概念和细节
分布式·zookeeper·kafka