一个服务器启动两个rabbitmq,核心答案是:通过环境变量隔离节点名、端口和数据目录,在一台机器的操作系统上直接部署两个独立实例,或用Docker容器分别映射不同端口。
这个问题在运维实操中很常见,尤其是项目联调阶段,一个环境需要隔离的MQ队列,或者测试环境机器有限,但必须区分生产配置和开发配置,很多人第一次遇到时,习惯性去改配置文件里的端口,结果发现第二个实例根本起不来,报错信息指向epmd或者节点名冲突,原因在于rabbitmq的实例标识不只是端口,还有节点名和数据目录,下面按实际操作的顺序,把这件事拆开讲清楚。
同一台服务器部署多个rabbitmq实例的前提
实例之间必须隔离的三样东西
第一个是节点名,RabbitMQ默认节点名是rabbit@hostname,两个实例如果都用这个名字,第二个启动时会被epmd判定为节点已存在,直接拒绝启动,所以每个实例必须设置独立的RABBITMQ_NODENAME。
第二个是端口,默认5672是AMQP协议端口,15672是管理界面端口,25672是集群通信端口,第二个实例必须换掉这三组端口,否则bind失败。
第三个是数据存储目录,Mnesia数据库、日志、插件缓存都写在RABBITMQ_MNESIA_BASE和RABBITMQ_LOG_BASE指向的路径,如果不改,两个实例会争抢同一个数据目录,导致启动成功后元数据错乱。
除此之外,还有一个容易忽略的坑:erlang cookie,多实例共用同一个.erlang.cookie文件是安全的,但如果是完全复制安装目录,需要确认cookie文件权限是400,属主正确,否则节点间握手失败,虽然单实例能启动,但后续如果想做同机集群会出问题。
前置检查:确认系统资源和端口占用
动手之前,先看两件事,第一,内存是否够用,每个rabbitmq实例默认占用大约200MB到400MB内存(含erlang虚拟机开销),两个实例就是翻倍,如果服务器本身只有2GB内存,跑两个实例很勉强,建议考虑Docker方式限制内存,第二,端口有没有被占用,用netstat -tlnp | grep -E '567|1567|2567'检查目标端口,确保新端口没有被其他服务占用。
bin目录复制法:同一套代码跑两个进程
具体操作步骤
这种方式的思路是复制一份rabbitmq的安装目录,通过修改环境变量让两个进程使用不同的运行时配置。
第一步,下载并解压rabbitmq的通用二进制包到两个不同的目录,
mkdir -p /opt/rabbitmq-node1 mkdir -p /opt/rabbitmq-node2 tar -xzf rabbitmq-server-generic-unix-3.13.7.tar.gz -C /opt/rabbitmq-node1 --strip-components=1 tar -xzf rabbitmq-server-generic-unix-3.13.7.tar.gz -C /opt/rabbitmq-node2 --strip-components=1
第二步,编辑每个实例的
etc/rabbitmq/rabbitmq-env.conf文件,node1保持默认端口,node2写入如下内容:
NODENAME=rabbit2@localhost RABBITMQ_NODE_PORT=5673 RABBITMQ_MNESIA_BASE=/var/lib/rabbitmq-mnesia-node2 RABBITMQ_LOG_BASE=/var/log/rabbitmq-node2
如果希望管理界面端口也区分开,node2的文件里还需要加一行:
RABBITMQ_MANAGEMENT_PORT=15673
第三步,分别启动,建议先启动node1,再启动node2:
/opt/rabbitmq-node1/sbin/rabbitmq-server -detached export RABBITMQ_MNESIA_BASE=/var/lib/rabbitmq-mnesia-node2 export RABBITMQ_LOG_BASE=/var/log/rabbitmq-node2 /opt/rabbitmq-node2/sbin/rabbitmq-server -detached
注意,node2启动前必须把环境变量导出到当前shell,否则rabbitmq-env.conf里的配置不生效。
第四步,验证两个实例都在运行:
/opt/rabbitmq-node1/sbin/rabbitmqctl status /opt/rabbitmq-node2/sbin/rabbitmqctl -n rabbit2@localhost status
-n参数指定节点名,这一步很多人会漏掉,不带-n时,rabbitmqctl默认连接rabbit@hostname,导致无法管理第二个实例。
管理界面和CLI的区分
两个实例都启用管理插件后,node1的管理地址是http://服务器IP:15672,node2的是http://服务器IP:15673,用户名密码可以分别设置:node1使用默认的guest/guest(仅限本机访问),node2在rabbitmq-env.conf所在目录的advanced.config里定义新用户,或者启动后用rabbitmqctl添加:
/opt/rabbitmq-node2/sbin/rabbitmqctl -n rabbit2@localhost add_user admin2 你的密码 /opt/rabbitmq-node2/sbin/rabbitmqctl -n rabbit2@localhost set_permissions -p / admin2 "." "." "."
Docker方式:隔离更彻底,配置更简单
用容器同时跑两个rabbitmq
如果服务器已经装好了Docker,这是最省事的方法,每条命令映射不同的端口和容器名,宿主机层面完全隔离。
启动第一个容器:
docker run -d --name rabbitmq1 -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin1 -e RABBITMQ_DEFAULT_PASS=密码1 -v /data/rabbitmq1:/var/lib/rabbitmq rabbitmq:3-management
启动第二个容器:
docker run -d --name rabbitmq2 -p 5673:5672 -p 15673:15672 -e RABBITMQ_DEFAULT_USER=admin2 -e RABBITMQ_DEFAULT_PASS=密码2 -v /data/rabbitmq2:/var/lib/rabbitmq rabbitmq:3-management
这里第二个容器把宿主机的5673映射到容器内部的5672,15673映射到15672,容器内部不需要做任何配置修改,因为每容器本身就是独立的rabbitmq实例。Docker方式特别适合在云服务器上快速搭建测试环境
,如果你买的云服务器配置不高,比如2核4G的入门机,跑两个容器加一个Nginx已经完全够用,没必要为这点需求去升级配置,云服务器价格差异在多数云厂商按年付费时能差出几百元,省下来的钱远高于这点内存开销。
两种方式的核心差异对比
| 对比项 | bin目录复制法 | Docker容器法 |
|---|---|---|
| 配置复杂度 | 需手动改环境变量和端口 | 一条docker run搞定 |
| 隔离程度 | 共享内核,共用erlang环境 | 进程和文件系统完全隔离 |
| 资源控制 | 无法单独限制内存 | 可加--memory参数限制 |
| 适合场景 | 服务器未装Docker,生产环境已有erlang | 测试环境、快速搭建、云服务器多租户 |
| 管理命令 | 需指定-n参数区分 |
docker exec进入容器分别管理 |
多实例部署后如何配置客户端连接
生产者消费者代码里的端口差异
当服务器上运行了两个rabbitmq,客户端连接时需要刻意指定端口,比如node1连接地址是amqp://账号:密码@服务器IP:5672,node2是amqp://账号:密码@服务器IP:5673。
如果使用Spring Boot,配置文件里同时定义两套连接:
spring:
rabbitmq:
host: 服务器IP
port: 5672
username: admin1
password: 密码1
rabbitmq2:
host: 服务器IP
port: 5673
username: admin2
password: 密码2
Java代码里用@RabbitListener注解时,通过containerFactory属性指定不同的SimpleRabbitListenerContainerFactory,就能把监听器分发到不同实例。
防火墙和安全组放行规则
这是很多人在云服务器上最容易卡壳的地方,本地测试两个实例都通,一放到公网就连接超时,原因是云厂商的安全组默认只放行了常用端口,需要去云控制台的安全组规则里,额外放行5673和15673两个TCP端口,简米云、酷番云、华为云的操作路径大同小异:进入ECS实例详情页,点击安全组,添加入方向规则,端口范围填5673/5673和15673/15673,授权对象填0.0.0/0(仅限测试环境)。
如果用的是CentOS服务器,还要检查firewalld:
firewall-cmd --permanent --add-port=5673/tcp firewall-cmd --permanent --add-port=15673/tcp firewall-cmd --reload
常见报错和排查思路
启动第二个实例时报节点名冲突
错误信息类似rabbitmq_node_monitor相关,或者提示node is already running,多数情况下是因为node2启动时忘了修改NODENAME
,检查方式:
ps aux | grep beam.smp
如果看到多个beam.smp进程但都带rabbit@hostname参数,说明节点名没区分开,杀掉所有rabbit进程,重新检查rabbitmq-env.conf里的配置再启动。
端口被占用导致启动失败
报错通常是tcp_listener相关,提示5673端口bind失败,用lsof -i:5673查一下端口归属,如果确认没有其他进程占用,很可能是SELinux拦截,临时关闭测试:
setenforce 0
或者给端口添加上下文:
semanage port -a -t rabbitmq_port_t -p tcp 5673
行业共识认为,SELinux是生产环境多实例部署最常见的隐藏障碍,配置完端口和目录权限后,最好用getenforce确认状态。
什么时候不该用单机多实例
一台服务器跑两个rabbitmq能解决资源紧张的问题,但前提是这两个实例都用于开发、测试或低并发场景,如果是核心生产业务,对可用性要求较高,多数情况下不建议在同一台服务器上做双实例替代集群,单机多实例意味着这台服务器挂了,两个MQ同时不可用,行业专家指出,生产环境的高可用方案应该是跨节点的rabbitmq集群,而不是把多个实例堆在同一台物理机上,如果预算实在有限,至少要用云服务器的主机快照定期备份数据目录,把恢复时间控制在分钟级别。
一个服务器怎么启动两个rabbitmq的Q&A
问:一个服务器启动两个rabbitmq,内存最低要求多少?
答:单个rabbitmq实例在空闲状态大约占用200MB左右内存,加载多个插件或出现消息堆积时会快速攀升,两个实例同时运行,建议服务器总内存不低于2GB,Docker方式可以分别设置--memory=512m来限制每个容器占用,防止单实例内存泄漏拖垮整台机器。
问:两个rabbitmq实例可以做同机集群吗?
答:可以,需要两个实例的.erlang.cookie内容完全一致,然后使用rabbitmqctl -n rabbit2@localhost stop_app、join_cluster rabbit@hostname、start_app三步完成组集群,同机集群的意义主要是利用多核CPU提升吞吐量,但不提供跨机器故障转移能力,若需要真正的高可用,仍建议至少两台服务器布局。
问:rabbitmq多实例部署后,数据目录可以共用吗?
答:绝对不可以,每个实例必须有独立的RABBITMQ_MNESIA_BASE目录,否则Mnesia启动时会检测到目录锁冲突,轻则数据错乱,重则节点崩溃,多实例部署时,目录划分是前提条件,docker方式需要在宿主机上创建两个不同的volume挂载点,二进制方式则直接修改rabbitmq-env.conf指定不同路径。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/719410.html





