ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RabbitMQ集群部署实战:高可用架构与故障排查全指南

RabbitMQ集群部署实战:高可用架构与故障排查全指南 RabbitMQ 集群部署说起来不算难网上教程一搜一大把但真正从零搭一套能扛业务、能平滑扩缩容、出问题了还能快速定位的集群我踩过的坑真不少。尤其是最近不少人用 Docker 镜像拉一个 RabbitMQ 起来Web 管理界面也能打开结果 admin 账号连虚拟主机都建不了或者节点之间始终无法通信这些都是非常典型的问题。这篇文章我就按实际运维的视角从集群规划、节点部署、权限配置、MQTT 接入到故障排查完整过一遍 RabbitMQ 集群部署方案。全程用的都是我在生产环境验证过的操作和参数尽量把每个“为什么这样做”都讲透希望能帮你少走弯路。1. 集群部署前先把这几个概念搞清楚1.1 RabbitMQ 集群到底解决了什么问题先说个最基础的问题为什么要组集群很多团队的 RabbitMQ 一开始就是单节点业务量小的时候完全没问题。但一旦流量上来或者你希望消息中间件具备高可用能力单节点的瓶颈就很明显进程挂了服务就断磁盘满了消息写不进去重启还要等一堆队列恢复。集群的价值说白了就是三件事——高可用、横向扩展、故障转移。高可用好理解一个节点挂了其他节点还能继续收发消息横向扩展是指通过增加节点提升整体吞吐和存储能力毕竟单机的内存和磁盘终归有限故障转移则是客户端连接断开后能自动重连到其他存活节点尽量让业务无感知。这里也顺带回答一个经常被问到的问题和 Kafka 比什么时候该选 RabbitMQ我个人看法是如果你的场景是高性能日志管道、海量流式数据、分区顺序保证优先考虑 Kafka而如果业务上是复杂的路由规则、延时消息、RPC 回调、需要精细确认机制的任务分发RabbitMQ 的灵活性和生态更合适。集群方案没有绝对优劣匹配业务才是关键。1.2 磁盘节点与内存节点你怎么选RabbitMQ 的节点分为磁盘节点和内存节点两种。很多初次搭建集群的人根本不看这个所有节点都默认启动成磁盘节点其实也没毛病。但如果你的集群规模超过三节点或者你希望某些节点承担更高吞吐就需要理解这两者的区别。磁盘节点会把队列元数据、交换机、绑定关系、用户权限等信息持久化到磁盘内存节点则只把元数据保存在内存里性能上确实有优势但一重启内存节点上的元数据就会丢失。注意内存节点只会丢弃元数据不会丢消息本身但节点重启后会尝试从磁盘节点同步元数据。问题是如果整个集群里所有的磁盘节点都挂了内存节点也活不下去。所以我的建议很直接生产环境所有节点都用磁盘节点不要为了那点性能去用内存节点。理由很简单元数据在 RabbitMQ 里本质上量很小哪怕是几千个交换机、队列、绑定关系也就是几 MB 到几十 MB 的规模内存节点的性能优势微乎其微。你省下的那点性能远不够赔偿一次元数据丢失带来的运维成本。队列层面的选型也要注意。RabbitMQ 的经典队列如果要做高可用需要配置镜像队列Mirrored Queue主从节点之间同步全量消息性能损耗明显。而新版本的 Quorum Queue基于 Raft 协议在一致性、数据安全上更好是官方推荐的替代方案。如果你们用的 RabbitMQ 版本在 3.8 以上新项目建议直接用 Quorum Queue别再用镜像队列了。提示集群节点自身的角色磁盘/内存与队列类型经典/Quorum是两套概念不要混淆。节点角色管的是元数据存储方式队列类型管的是消息数据的复制策略。2. 集群前置准备与部署方案选型2.1 直接用二进制包部署还是上 Docker这是很多人纠结的第一个问题。先给结论没有绝对答案看你的环境和管理习惯。如果你们公司是有专门运维团队的服务器上跑了一堆 Java、Tomcat那用官方二进制包部署最干净可控性最强。如果你想快速拉起一套环境做测试或者你们本身就已经全面容器化了那用 Docker 部署会省心很多。不过要注意Docker 部署 RabbitMQ 集群最大的坑就是.erlang.cookie和节点名的解析。容器默认的 hostname 每次创建都可能变节点间的通信会受影响。所以用 Docker 部署集群时一定要显式指定 hostname并挂载或手动指定 cookie 文件。这里我不做“谁好谁坏”的结论下面两章我会分别给出原生部署和 Docker Compose 部署两套完整流程你按自己的场景选一套就行。2.2 主机规划、端口清单与文件目录假设你准备搭建一个三节点集群我先给一份比较标准的主机规划后续所有操作都以这三台为例节点IP节点名角色node1192.168.1.11rabbitnode1磁盘节点node2192.168.1.12rabbitnode2磁盘节点node3192.168.1.13rabbitnode3磁盘节点RabbitMQ 涉及的端口不少规划时记得在防火墙和安全组里提前放行4369Erlang 端口映射守护进程epmd使用5672AMQP 主端口客户端连接用15672Web 管理界面端口25672集群节点间通信端口1883MQTT 插件开启后的默认端口15675Web MQTT 端口如果启用提示实际生产环境最好做一个端口用途清单避免后期排查时自己都分不清哪个端口是干嘛的。特别是 25672 容易被忽略节点间通信不通大多数时候就是它没放行。文件层面二进制部署时会用到几个关键路径/etc/rabbitmq/rabbitmq.conf主配置文件/etc/rabbitmq/enabled_plugins启用的插件列表/etc/rabbitmq/.erlang.cookie集群通信的共享密钥/var/log/rabbitmq/日志目录2.3 .erlang.cookie 与节点名决定集群能不能拉起的关键RabbitMQ 节点间的通信依赖 Erlang 分布式架构而 Erlang 分布式节点要互相认证靠的就是.erlang.cookie。这个文件里面就是一行字符串相当于集群节点间的“共享密码”。集群里所有节点的 cookie 必须完全一致哪怕差一个字符节点都无法加入集群。还有一个容易被忽略的细节节点的 hostname 解析。RabbitMQ 启动时会将主机名解析成完整的 Erlang 节点名比如rabbitnode1。如果/etc/hosts里没有把node1映射到对应 IP或者 DNS 解析有问题节点之间互相找不到集群就拉不起来。所以搭建集群前我建议三台机器都配置好/etc/hosts比如192.168.1.11 node1 192.168.1.12 node2 192.168.1.13 node3这两件事看起来基础但实际排查中发现cookie 不一致和hosts 配置错误占到了集群拉起失败原因的七八成。3. 三节点集群原生部署实操CentOS / Ubuntu 通用3.1 安装 Erlang 与 RabbitMQ ServerRabbitMQ 依赖于 Erlang 环境版本必须匹配。官方有一个兼容性列表比如 RabbitMQ 3.13.x 对应 Erlang 26.x。这里以 Ubuntu 22.04 / CentOS 7 为例最简单的方式是添加官方仓库# Ubuntu / Debian curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo apt-key add - echo deb https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-erlang/deb/ubuntu jammy main | sudo tee /etc/apt/sources.list.d/rabbitmq.list sudo apt update sudo apt install -y erlang rabbitmq-serverCentOS 用 rpm 包安装时我建议提前把socat、logrotate等依赖装好不然启动时会报错。装完先不急着启动先把集群要用的 cookie 和 hosts 配好。3.2 配置 cookie、hosts 与 rabbitmq.conf三台节点都执行以下操作# 1. 设置 hostname sudo hostnamectl set-hostname node1 # node2、node3 分别是 node2、node3 # 2. 配置 hosts cat /etc/hosts EOF 192.168.1.11 node1 192.168.1.12 node2 192.168.1.13 node3 EOF # 3. 生成一致的 cookie先在 node1 生成再复制到 node2/node3 sudo mkdir -p /etc/rabbitmq echo MY-SECURE-COOKIE-STRING-2024 | sudo tee /etc/rabbitmq/.erlang.cookie sudo chown rabbitmq:rabbitmq /etc/rabbitmq/.erlang.cookie sudo chmod 400 /etc/rabbitmq/.erlang.cookie注意.erlang.cookie权限必须设置成 400 或 600属主必须是运行 RabbitMQ 的用户一般是rabbitmq。如果权限不对Erlang 会直接拒绝使用这个文件表现为节点无法启动。主配置文件写这些内容三台节点都一样cat /etc/rabbitmq/rabbitmq.conf EOF listeners.tcp.default 5672 management.tcp.port 15672 cluster_formation.peer_discovery_backend classic_config cluster_formation.classic_config.nodes.1 rabbitnode1 cluster_formation.classic_config.nodes.2 rabbitnode2 cluster_formation.classic_config.nodes.3 rabbitnode3 EOFcluster_formation这一段是让 RabbitMQ 在启动时自动发现集群节点填上全部节点名即可。具体原理后续会讲。3.3 启动第一个节点并初始化集群第一台节点不要急着加集群它负责创建最初的集群元数据sudo systemctl start rabbitmq-server sudo systemctl enable rabbitmq-server sudo rabbitmqctl cluster_status看到Disk Nodes里有rabbitnode1说明第一个节点已经正常以磁盘节点身份运作了。接着在 node2 上执行sudo systemctl start rabbitmq-server sudo rabbitmqctl stop_app sudo rabbitmqctl join_cluster rabbitnode1 sudo rabbitmqctl start_appstop_app的意思是只停止 RabbitMQ 应用而不是停掉 Erlang 虚拟机这样节点才能以空的状态加入集群。join_cluster rabbitnode1指定要加入的现有集群节点。默认加入后就是磁盘节点如果你希望它以内存节点加入可以加--ram参数但我前面说了生产环境不建议。node3 重复同样的操作。全部完成后随便挑一个节点执行sudo rabbitmqctl cluster_status输出里应该能看到三个节点都列出来了而且每个节点的分区健全。执行rabbitmqctl list_cluster_nodes能看到节点类型。心得加入集群时如果卡住不动99% 是 cookie 或 hosts 配置问题。可以先看/var/log/rabbitmq/下节点的启动日志里面会明确告诉你哪个节点连接失败。3.4 配置镜像队列与 Quorum Queue 策略集群建好之后默认的经典队列并不会自动做高可用。对经典队列而言需要手动配置镜像策略否则消息只会存储在单个节点上。用管理命令添加策略sudo rabbitmqctl set_policy ha-all ^ {ha-mode:all,ha-sync-mode:automatic}这条命令的意思是对所有名称匹配^即全部队列的队列启用镜像模式副本分布到所有节点同步方式为自动。但如果你用的是 Quorum Queue不需要配置镜像策略因为它是天然复制到多节点的你只需要在声明队列时指定类型rabbitmqadmin declare queue nameq.queue durabletrue arguments{x-queue-type:quorum}从实际生产经验看Quorum Queue 在节点故障恢复时的表现确实更稳定我建议新项目优先采用。经典队列 镜像策略这种组合适合老项目里已经被大量使用的场景迁移成本太大时才保留。4. Docker Compose 部署集群以及那个让人头大的 admin 账号权限问题4.1 用 Docker Compose 拉起三节点集群如果你更习惯容器化部署这里给一份可以直接用的docker-compose.yml。需要注意的是每个容器必须显式设置hostname并且把.erlang.cookie统一挂载进去否则集群节点间无法通信。version: 3.8 services: rabbit1: image: rabbitmq:3.13-management hostname: rabbit1 container_name: rabbit1 environment: - RABBITMQ_ERLANG_COOKIESECRET_COOKIE_VALUE - RABBITMQ_NODENAMErabbitrabbit1 ports: - 5672:5672 - 15672:15672 volumes: - rabbit1_data:/var/lib/rabbitmq networks: - rabbitnet rabbit2: image: rabbitmq:3.13-management hostname: rabbit2 container_name: rabbit2 environment: - RABBITMQ_ERLANG_COOKIESECRET_COOKIE_VALUE - RABBITMQ_NODENAMErabbitrabbit2 ports: - 5673:5672 - 15673:15672 volumes: - rabbit2_data:/var/lib/rabbitmq networks: - rabbitnet depends_on: - rabbit1 rabbit3: image: rabbitmq:3.13-management hostname: rabbit3 container_name: rabbit3 environment: - RABBITMQ_ERLANG_COOKIESECRET_COOKIE_VALUE - RABBITMQ_NODENAMErabbitrabbit3 ports: - 5674:5672 - 15674:15672 volumes: - rabbit3_data:/var/lib/rabbitmq networks: - rabbitnet depends_on: - rabbit1 volumes: rabbit1_data: rabbit2_data: rabbit3_data: networks: rabbitnet: driver: bridge启动后进入 rabbit1 容器把 rabbit2 和 rabbit3 加入集群docker exec -it rabbit2 rabbitmqctl stop_app docker exec -it rabbit2 rabbitmqctl join_cluster rabbitrabbit1 docker exec -it rabbit2 rabbitmqctl start_app docker exec -it rabbit3 rabbitmqctl stop_app docker exec -it rabbit3 rabbitmqctl join_cluster rabbitrabbit1 docker exec -it rabbit3 rabbitmqctl start_app4.2 为什么 Docker 部署后admin 账号还是不能创建虚拟主机这是热词里出现频率最高的问题管理界面打开了用 admin 用户登进去点Virtual Hosts那边却无法创建新的虚拟主机。这是为什么关键在于 RabbitMQ 的用户权限模型用户能不能创建虚拟主机不看你是不是 admin而是看你有没有administrator标签并且是否对目标虚拟主机有配置权限。Docker 官方镜像默认会创建一个用户这个用户的信息由环境变量决定例如environment: - RABBITMQ_DEFAULT_USERadmin - RABBITMQ_DEFAULT_PASSadmin123 - RABBITMQ_DEFAULT_VHOST/如果你只设置了默认用户和密码没有给用户打administrator标签那么即使你能登录 Web 管理界面也只能看到和操作自己有权限的虚拟主机而没有管理全局的权限。这也是为什么很多人觉得“管理界面能用但 admin 什么都干不了”。所以Docker Compose 的环境变量里在一开始就要把用户权限打满environment: - RABBITMQ_DEFAULT_USERadmin - RABBITMQ_DEFAULT_PASSadmin123 - RABBITMQ_DEFAULT_VHOST/ - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS-rabbitmq_management load_definitions /etc/rabbitmq/definitions.json但这种做法需要在容器启动时加载一份定义文件对快速验证来说略麻烦。更直接的办法是容器起来之后用命令手动处理# 进入容器 docker exec -it rabbit1 bash # 创建虚拟主机如果你不想用默认的 / rabbitmqctl add_vhost myvhost # 设置用户为 administrator 标签 rabbitmqctl set_user_tags admin administrator # 给用户在指定 vhost 上授予全部权限 rabbitmqctl set_permissions -p / admin .* .* .* rabbitmqctl set_permissions -p myvhost admin .* .* .*关于权限的三个正则第一个.*允许配置交换机、队列的创建删除第二个.*允许写入发送消息第三个.*允许读取消费消息如果你只给了一部分权限比如只给写不读那客户端能发消息但收不到消息排查时会非常困惑。4.3 通过 Web 管理界面验证权限配置配置完成后重启 RabbitMQ 应用或者等几秒让权限策略生效重新登录 Web 管理界面。此时在Admin标签页里能看到用户列表点击自己的账号确认Tags中包含administrator。然后在Virtual Hosts标签页里应该能看到/和myvhost都在列表里且后面有权限标志。这里有个经验不要过度依赖 Web 界面做权限管理。Web 界面适合查看批量操作和精确配置用rabbitmqctl比在界面上点来点去高效得多也更容易写进自动化脚本。我自己管理集群时用户和权限的增改基本都是命令行一把梭Web 界面只看监控和队列状态。5. MQTT 插件开启以及集群的对外负载均衡5.1 启用 MQTT 插件并用 MqttX 连接RabbitMQ 不仅可以做 AMQP 消息中间件还可以直接当 MQTT Broker 用这对物联网场景特别友好。开启方式# 原生部署 sudo rabbitmq-plugins enable rabbitmq_mqtt rabbitmq_web_mqtt # Docker 部署 docker exec -it rabbit1 rabbitmq-plugins enable rabbitmq_mqtt rabbitmq_web_mqtt开启后默认监听1883端口15675端口用于 WebSocket 方式接入。如果你要用 MqttX 测试连接填这几项就可以Host:192.168.1.11Port:1883Username:mqtt_userPassword:mqtt_pass注意MQTT 连接同样受权限控制。如果mqtt_user没有在对应 vhost 上的写读权限连接虽然能建立但发布或订阅会直接被拒绝。所以创建 MQTT 用户后一定要执行rabbitmqctl add_user mqtt_user mqtt_pass rabbitmqctl set_user_tags mqtt_user management rabbitmqctl set_permissions -p / mqtt_user .* .* .*MqttX 连接成功的前提就是这三行命令都正确。5.2 三节点前加一层负载均衡客户端该连谁集群搭建好之后客户端不能写死连某一个节点的 IP否则这个节点挂了客户端不会自动切换。生产环境我建议在前面加一层负载均衡最简单的就是 HAProxy。HAProxy 配置里关键是做四层 TCP 代理并启用对节点端口的健康检查frontend rabbitmq_front bind *:5672 mode tcp default_backend rabbitmq_nodes backend rabbitmq_nodes mode tcp balance roundrobin option tcp-check server rabbit1 192.168.1.11:5672 check inter 3s fall 2 rise 2 server rabbit2 192.168.1.12:5672 check inter 3s fall 2 rise 2 server rabbit3 192.168.1.13:5672 check inter 3s fall 2 rise 2客户端只需要连接 HAProxy 的 5672 端口后面任何一个 RabbitMQ 节点挂了HAProxy 的check会发现后端不可用自动把流量导到其他存活节点。inter 3s表示每 3 秒探测一次fall 2表示连续失败 2 次标记为下线。5.3 集群节点的故障转移与脑裂处理集群节点之间的网络如果出现分区RabbitMQ 会进入网络分区状态。默认配置下分区恢复后集群会自动处理但可能出现某些节点被踢出集群并拒绝重新加入的情况。处理方式有两种重启被分区的节点让它重新加入集群如果节点状态很乱先rabbitmqctl stop_app然后rabbitmqctl reset再重新join_cluster和start_app关于脑裂的预防核心是网络质量。RabbitMQ 集群本身对网络抖动比较敏感建议集群节点都放在同一机房或同一 VPC 内节点间走内网不要跨公网组集群。跨地域的场景应该用 Federation 或 Shovel 插件做消息转发而不是直接一个集群打天下。6. 高频问题排查与避坑速查这部分我把热词里那些典型的坑整理成表格按“现象-原因-解法”的方式写清楚。现象原因排查与解法管理界面能打开但 admin 无法创建虚拟主机用户缺少administrator标签或 vhost 权限执行rabbitmqctl set_user_tags admin administrator再用set_permissions授予权限rabbitmqctl 能创建用户但 Web 界面显示“不能连接到服务器”管理插件未启用或控制台节点名不匹配执行rabbitmq-plugins enable rabbitmq_management确认rabbitmqctl与 Web 连的是同一节点节点加入集群卡住或 cluster_status 里节点看不到彼此.erlang.cookie不一致或 hosts 解析异常三台节点统一 cookie校验/etc/hosts与节点名查看日志确认具体报错RabbitMQ 启动失败提示init:unable to read cookiecookie 文件权限不对或内容为空确认属主为 rabbitmq权限 400内容非空Windows 上安装后启动失败服务无法启动Erlang 版本不匹配或安装目录有中文/空格安装与 RabbitMQ 兼容的 Erlang安装路径不要用中文用管理员权限运行服务Docker 重启后集群节点失联容器 hostname 变化或 cookie 没挂载固定 hostname统一挂载 cookie 文件客户端能连接但无法消费消息用户对 vhost 缺少读权限执行set_permissions给足第三段正则权限MQTT 连接成功但发不了消息MQTT 用户没有 vhost 写权限给 MQTT 用户配置对应 vhost 的写权限这里再单独强调两个细节。第一个是 Windows 上的 RabbitMQ。很多人在本地开发环境用 Windows 装 RabbitMQ总在服务启动阶段翻车。踩过几次坑之后我的经验是两个一是要去官网查清楚当前 RabbitMQ 版本依赖的 Erlang 版本不要随便装最新版 Erlang版本不对服务起不来二是安装路径和工作目录不要出现中文、空格最好直接用C:\RabbitMQ这种简单路径。装完之后用管理员权限打开命令行执行rabbitmq-service.bat start不成功就去看%APPDATA%\RabbitMQ\log\下的启动日志。第二个是关于管理界面显示“不能连接到服务器”的情况。这个在 Docker 镜像里特别容易出现因为容器里rabbitmqctl默认会去连名为rabbithostname的节点但如果你在环境变量里设置了RABBITMQ_NODENAME名称不匹配rabbitmqctl就找不到节点了。解决办法是执行rabbitmqctl时加上-n参数指定节点名或者直接用容器里默认的节点名不要随意修改。7. 集群部署完成后还需要做这几件事集群能跑起来只是第一步。按我个人的经验部署完成后一定要做一轮“验收测试”确认高可用能力真的有效而不是纸面上看起来是集群。第一件事验证节点故障转移。挑一个节点直接停掉看客户端是否能在短时间内自动重连到其他节点。你可以用一个简单的生产者消费者脚本压一会儿观察停节点期间消息是否丢失、消费是否有明显中断。如果中断时间过长检查一下客户端的重连机制和连接工厂配置确认automatic recovery已开启并且连接地址配置了多个节点或负载均衡。第二件事确认消息堆积和队列同步情况。向集群发送一批消息然后执行rabbitmqctl list_queues name messages messages_ready messages_unacknowledged观察队列在各节点上的分布。对于 Quorum Queue可以查看rabbitmqctl list_quorum_queue_stats确认所有队列的leader和online状态都健康。第三件事监控要跟上。RabbitMQ 官方提供了rabbitmq-prometheus插件开启后暴露 15692 端口Prometheus 可以直接抓取指标。我的建议是至少把以下指标接入告警节点可用性队列消息堆积数量连接数文件描述符使用率内存使用率磁盘剩余空间告警阈值没有统一标准但有一个原则磁盘快满和高水位内存一定要提前告警别等到消息写不进去才去处理。第四件事做好备份。RabbitMQ 的元数据用户、虚拟主机、策略、绑定关系可以通过rabbitmqctl export_definitions导出成 JSON 文件建议定期备份。消息数据则依赖节点存储靠队列副本机制保证安全。心得很多团队把集群搭起来就以为万事大吉实际上节点故障、网络抖动、客户端异常重连每一个环节都可能暴露问题。我见过最惨的事故不是节点全挂而是磁盘节点先挂、内存节点随后也挂整个集群元数据全丢。所以磁盘节点的作用再怎么强调也不过分。8. 最后分享一点个人经验关于 RabbitMQ 集群部署我最后再说两个实际操作中得到的经验。第一个是关于升级策略。RabbitMQ 集群不支持跨大版本热升级比如 3.12 升 3.13 没问题但 3.12 跳到 4.0 就很可能不兼容。升级时要采用“滚动升级”的思路先升级一个节点确认集群状态正常再依次升级其他节点。升级前一定要先备份配置和元数据并且千万不能同时升级 Erlang 和 RabbitMQ 两个东西否则出了问题你根本不知道是谁导致的。第二个是关于“能简单就别复杂”。如果你只有两三台机器业务量也不算大真没必要把集群方案设计得特别复杂。我曾经见过有人用虚拟机搭了五个 RabbitMQ 节点还做了跨机房的镜像结果日常运维成本高得离谱最后又降配回三节点。集群规模应该从业务需求反推而不是为了“看起来高可用”盲目堆节点。希望这篇 RabbitMQ 集群部署方案能帮到你。如果你刚接触集群建议先在两台机器上完整走一遍原生部署流程再试 Docker 部署等把两种方法的差异和坑都摸清了再去谈生产环境的高可用和自动化运维。
返回列表