多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

RabbitMQ从安装到排障:Erlang版本匹配、配置调优与生产部署实践

RabbitMQ从安装到排障:Erlang版本匹配、配置调优与生产部署实践 接手一台新Linux服务器任务是把RabbitMQ装起来。这件事听起来简单真正闷头做一遍你就会发现坑基本都在暗处。Erlang版本不匹配、hostname解析不了、默认guest账号只能本机登录、内存阈值一触发就自动阻塞连接……每一个都让人想摔键盘。我把这次安装RabbitMQ的完整过程连同排查思路一起沉淀出来从版本选择到仓库安装从配置调优到故障排查一条线走通。如果你正准备在Ubuntu、CentOS或Debian上部署RabbitMQ或者已经被“启动失败”折磨过这篇应该能帮你少走不少弯路。1. 装之前先想清楚三件事1.1 RabbitMQ和Erlang的版本匹配是头等大事RabbitMQ是用Erlang写的所以它对Erlang版本极其敏感。这不是“能跑就行”的关系而是“版本不对直接起不来”的硬依赖。很多人上来就apt install erlang装完再装RabbitMQ启动时报一堆看不懂的BEAM错误核心原因就是Erlang版本不匹配。Erlang语言自己还分大版本RabbitMQ每个版本只支持一段特定范围内的Erlang。我建议你动手之前先打开RabbitMQ官网的“Which Erlang versions”页面那里有详细的兼容矩阵。简单说RabbitMQ 3.12和3.13系列通常搭配Erlang 25或26旧一点的3.8系列搭配Erlang 22到24。别用太新的Erlang也别用太老的就卡在官方支持区间内最稳。比如我这台服务器部署的是RabbitMQ 3.13搭配的Erlang就是26.x。这个匹配关系决定了你后面所有操作流程。1.2 安装方式怎么选仓库、二进制包还是容器这三个方向我都试过适合的场景完全不同。系统发行版的软件源仓库安装最省心依赖自动解决还有systemd脚本属于“默认选项”。但仓库里的版本往往偏旧比如Ubuntu 20.04默认源里的RabbitMQ还是3.8如果你不需要新特性这反而是稳定优势。通用二进制包安装路径可控、版本任选适合离线环境和公司内部源但需要手工处理用户、目录权限和systemd服务麻烦一点。Docker方式隔离性最好几十秒就能起一个实例适合本地开发和CI但生产环境如果还要管集群、调内核参数、配网络容器化会额外增加一层复杂度。我第一次生产部署用的是二进制包因为公司内网环境无法访问外网仓库。后来自己搭测试环境我反而直接用apt源速度最快。我的建议是生产环境求稳优先官方仓库或二进制包开发测试图省事Docker完全够用。1.3 服务器基础环境清单装RabbitMQ之前服务器有几个基础条件必须满足否则后面全是坑。第一主机名必须能解析。RabbitMQ节点非常依赖hostname你可以用hostname命令查看再确认/etc/hosts里有对应的记录。很多“启动失败”案例最后查出来都是hostname解析不了Erlang节点间通信直接失败。第二内存建议至少2GB。RabbitMQ默认的内存水位阈值是0.4也就是物理内存的40%低于这个值会触发内存告警并阻塞生产者。512MB内存的机器跑起来非常难受动不动就告警。第三提前规划好端口。RabbitMQ主要用到4369epmd、5672AMQP、15672Web管理界面、25672集群通信。这些端口在内网要放行公网一律不要暴露。把这几件事在装之前确认好后面你才会发现安装本身其实很顺利。2. Erlang环境准备把地基打好2.1 为什么推荐用ESL仓库装ErlangLinux发行版自带的Erlang包来源是Debian/Ubuntu官方源版本更新速度慢而且经常和RabbitMQ需要的版本区间错位。比如Debian 11自带的Erlang是24你装RabbitMQ 3.12还是3.13版本支持都对得上但老系统就不行了比如CentOS 7默认源里的Erlang版本很老装新版RabbitMQ基本必挂。这时候Erlang Solutions提供的ESL仓库就派上用场了。这个仓库专门维护多个版本的Erlang更新及时、版本选择灵活是官方文档里多次推荐的方式。当然也有另一条路用RabbitMQ官方提供的rabbitmq-server仓库安装RabbitMQ时它并不会自动帮你装对应的Erlang所以你得提前把Erlang准备到位。这也是我把Erlang单独拿出来写一整章的原因——它在整个安装链条里其实是第一道关卡。2.2 实操添加ESL仓库并安装指定Erlang版本下面以Debian/Ubuntu系统为例演示一下具体流程。不同发行版命令稍有区别但思路完全一致。# 安装依赖工具 sudo apt-get update sudo apt-get install -y curl gnupg apt-transport-https # 下载并安装ESL仓库的deb包 curl -fsSL https://packages.erlang-solutions.com/debian/erlang_solutions.asc | sudo gpg --dearmor -o /usr/share/keyrings/erlang-solutions.gpg echo deb [signed-by/usr/share/keyrings/erlang-solutions.gpg] https://packages.erlang-solutions.com/debian $(lsb_release -sc) contrib | sudo tee /etc/apt/sources.list.d/erlang-solutions.list # 更新源并查看可用的Erlang版本 sudo apt-get update apt-cache policy esl-erlang这里有个经验ESL仓库里esl-erlang包默认会拉最新版但你不需要最新。执行apt-cache policy esl-erlang看清当前仓库里有哪些版本然后指定大版本安装比如我需要Erlang 26系列# 安装指定Erlang 26版本 sudo apt-get install -y esl-erlang1:26.2.5-1装完之后把erl软链到系统路径方便后面使用sudo ln -s /usr/lib/erlang/bin/erl /usr/local/bin/erl erl -version2.3 验证安装结果Erlang装完要立刻验证别等RabbitMQ启动才发现问题。运行erl -version或者直接进交互式shell再退出如果能正常显示版本号说明Erlang运行时没问题。另外可以用erl -noshell -eval io:format(~s~n, [erlang:system_info(otp_release)]), halt().查看OTP版本RabbitMQ兼容性矩阵里查的就是这个OTP版本号。我在CentOS上的做法是直接用yum install erlang但CentOS默认源里的Erlang版本可能只有23这时要去ESL仓库找对应RPM包。无论哪个发行版记住核心原则先确认OTP版本再决定RabbitMQ版本。顺序搞反了后面就是无限重新安装的循环。3. RabbitMQ安装实操三种方式全记录3.1 方案A官方仓库安装生产推荐Team RabbitMQ维护了自己的apt仓库装起来比ESL还简单而且升级路径清晰。先配置仓库再直接安装。# 导入RabbitMQ官方签名密钥 curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/rabbitmq-signing-key.gpg # 添加apt源 echo deb [signed-by/usr/share/keyrings/rabbitmq-signing-key.gpg] https://ppa1.rabbitmq.com/rabbitmq/rabbitmq-erlang/debian/$(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/rabbitmq.list # 更新源并安装 sudo apt-get update sudo apt-get install -y rabbitmq-server这里需要注意RabbitMQ官方仓库里其实分两个子仓库一个是distribution直接提供rabbitmq-server二进制包另一个是rabbitmq-erlang仓库可以替代ESL仓库来装Erlang。如果你已经在系统里有一个Erlang版本建议不让RabbitMQ仓库管理Erlang避免冲突。一切正常的话安装完服务会自动注册到systemd直接用sudo systemctl start rabbitmq-server sudo systemctl status rabbitmq-server看到active (running)就说明服务起来了。我用这个方式装过3.13.3版本整个过程5分钟搞定连端口都已经自动监听好了。3.2 方案B通用二进制包安装离线环境首选内网服务器没外网权限的用二进制包最合适。步骤也不复杂关键是按顺序来。先从官网下载rabbitmq-server-generic-unix对应的版本放到服务器上解压。# 建议用固定版本号比如3.13.3 wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.13.3/rabbitmq-server-generic-unix-3.13.3.tar.xz # 解压到指定目录 sudo mkdir -p /usr/lib/rabbitmq sudo tar -xf rabbitmq-server-generic-unix-3.13.3.tar.xz -C /usr/lib/rabbitmq --strip-components1 # 添加系统用户不以root运行 sudo useradd -r -s /bin/false rabbitmq sudo chown -R rabbitmq:rabbitmq /usr/lib/rabbitmq启动时先设置环境变量然后以rabbitmq用户运行export PATH$PATH:/usr/lib/rabbitmq/sbin sudo -u rabbitmq /usr/lib/rabbitmq/sbin/rabbitmq-server -detached二进制的目录结构是独立的默认配置文件和Mnesia数据目录都会生成在$RABBITMQ_HOME下面想改数据目录需要设置RABBITMQ_MNESIA_BASE等环境变量。这种安装方式最大的好处是“干净利落”你完全知道RabbitMQ装在了哪里、用了哪个版本、依赖什么。缺点是systemd要自己写而且升级要重新下载包。3.3 方案C容器安装开发测试利器开发环境里我懒得折腾依赖时直接一条命令解决docker run -d --name rabbitmq \ -p 5672:5672 -p 15672:15672 \ -e RABBITMQ_DEFAULT_USERadmin \ -e RABBITMQ_DEFAULT_PASSadmin123 \ rabbitmq:3.13-management注意别用默认rabbitmq:latest镜像那个不带web管理插件。要带管理功能就得用带management标签的镜像。另一个细节是把数据目录挂载出来否则容器一删数据全没-v rabbitmq_data:/var/lib/rabbitmq容器方式对学习、联调、测试非常友好但生产环境如果你要跑集群加插件、调和内核参数容器会带来一定程度的不透明性。我个人观点是生产环境用虚拟机或物理机上的systemd方式更直接容器适合当“临时环境”用。3.4 三种方式对比选型经验总结我整理了一张表方便你按自己场景做决定安装方式适用场景优点缺点官方仓库生产环境、内外网都可访问依赖自动解决、systemd集成、升级方便版本受仓库源限制不一定最新二进制包离线内网、定制部署版本自由、路径可控、行为透明需手工配用户和systemd升级麻烦Docker容器开发测试、临时环境秒级启动、环境隔离、易清理生产集群复杂日志和调优不直观我现在的习惯是生产用apt源内部离线环境用二进制包自己电脑上写demo用Docker。三套方案并不互斥按需求切换就行。4. 启动、配置与用户权限管理4.1 启动服务与确认状态仓库安装完启动过程很简单。但启动成功不等于一切正常你要去确认状态。执行rabbitmqctl status这个命令回显的信息很密重点看几个字段Runtime显示Erlang版本RabbitMQ version显示当前版本Node name是节点名Listeners下面应该能看到AMQP和HTTPS监听。如果你能看到一条alarms: (none)说明没有内存或磁盘告警这才是真正的“一切正常”。我在第一次装的时候看到active (running)就觉得完事了结果连上5672端口试生产消息一直超时排查半天才发现是防火墙没放行。所以启动确认最好再加一步——用telnet 127.0.0.1 5672或者ss -lntp | grep 5672确认端口真的通了。这一步不要省。4.2 核心配置文件 rabbitmq.conf 解读与修改新版RabbitMQ的配置文件是/etc/rabbitmq/rabbitmq.conf它的语法是Key-Value形式跟老版rabbitmq.config的Erlang Term语法完全不同。很多人从老教程抄来一段rabbitmq.config内容放到新版本里发现根本不生效原因就在这里。基础配置我一般这样写# 监听内网IP的5672端口 listeners.tcp.default 5672 # Web管理界面端口 management.tcp.port 15672 # 内存水位阈值默认0.4生产环境调高到0.6 vm_memory_high_watermark.relative 0.6 # 磁盘剩余空间阈值默认1.0GB disk_free_limit.absolute 2GB # 默认guest用户只能本地访问这行允许从指定IP段访问 loopback_users.guest false这里重点说下内存水位。RabbitMQ有个机制当内存使用超过阈值它会阻塞所有生产者的连接直到内存降下来。默认0.4意味着如果你服务器有16GB内存Ruby进程只要超过6.4GB就会停止接收消息。这其实是保护机制但如果你机器上还跑了别的应用很容易误伤所以要根据实际业务量调。我一般调到0.6同时保证系统还有余量给操作系统和其他进程。配置文件改完要重启服务才生效这个不用说但要提醒的是重启会断开所有客户端的连接生产环境需要提前做业务低峰期计划。4.3 创建用户、虚拟主机与权限控制RabbitMQ默认有个guest账号密码也是guest。但有个很坑的限制guest只允许从localhost连接远程访问直接用普通客户端连接必失败。所以必须创建自己的管理员账号。我常用的一套命令# 创建用户 rabbitmqctl add_user admin StrongPass123 # 给管理员标签 rabbitmqctl set_user_tags admin administrator # 给/虚拟主机全部权限 rabbitmqctl set_permissions -p / admin .* .* .*权限标签里的三组正则分别对应配置权限configure、写权限write、读权限read。我这里用了.*也就是全部放开。生产环境更讲究一点单个业务应该用单独的虚拟主机和单独的用户权限也尽量收敛。比如# 创建业务专属vhost rabbitmqctl add_vhost order_service # 给指定用户分配这个vhost的权限 rabbitmqctl set_permissions -p order_service order_app .* .* .*用虚拟主机做隔离的好处是一套RabbitMQ可以给不同团队或不同环境共用互不干扰。这个管理思路比给所有业务开一个/要规范得多。我自己踩过的坑是直接在某个业务代码里用了guest代码上线后怎么都连不上日志报ACCESS_REFUSED换成专属账号和vhost之后一切正常。4.4 开启Web管理插件没有可视化界面没法直观查看队列堆积和连接数所以插件基本必装rabbitmq-plugins enable rabbitmq_management启用之后访问http://服务器IP:15672就能看到登录页面。注意插件启用之后同样要对rabbitmq service做一次重启确保监听正常。然后立刻做两件事第一用刚才创建的管理员账号登录第二登录后修改guest密码或者干脆把guest删掉。Web界面上能看到Queue消息数、Connection连接数、Channel数量、内存和磁盘占用这个面板对运维排查太关键了好多生产问题一眼就能定位方向。5. 生产环境部署的额外功课5.1 用systemd托管并设置开机自启仓库安装的RabbitMQ自带systemd服务单元二进制包则要自己写。很多人装完二进制包之后直接手动rabbitmq-server -detached服务器重启后服务就起不来了。我写了一个可以直接用的unit文件路径是/etc/systemd/system/rabbitmq-server.service内容如下[Unit] DescriptionRabbitMQ Broker Afternetwork.target [Service] Typesimple Userrabbitmq Grouprabbitmq WorkingDirectory/usr/lib/rabbitmq ExecStart/usr/lib/rabbitmq/sbin/rabbitmq-server ExecStop/usr/lib/rabbitmq/sbin/rabbitmqctl shutdown Restarton-failure RestartSec10 LimitNOFILE65535 [Install] WantedBymulti-user.target把unit文件放好后执行sudo systemctl daemon-reload sudo systemctl enable rabbitmq-server sudo systemctl start rabbitmq-serverLimitNOFILE65535这行特别重要。RabbitMQ作为消息中间件会打开大量文件句柄默认的1024完全不够用高并发下会出现Too many open files或者连接被强制断开。除了在systemd里限制还要在/etc/security/limits.conf里给rabbitmq用户配置rabbitmq soft nofile 65535 rabbitmq hard nofile 655355.2 内核参数与性能调优建议RabbitMQ对内核参数有一些隐性要求不调也能跑但高负载下会出问题。我用阿里云和自建机房的经验来看有3个参数是重点第一net.core.somaxconn。这个值控制TCP连接排队的上限默认是128或4096看系统和内核版本。RabbitMQ接受的TCP连接数可能瞬间飙升建议提高到1024以上echo net.core.somaxconn 1024 /etc/sysctl.conf sysctl -p第二vm.swappiness。内存充足的情况下最好让操作系统尽量不要swap把匿名页换出到磁盘会严重拖慢Erlang的运行时性能。建议设置成10以下。如果你服务器内存充足且RabbitMQ独占可以直接调成0。第三TCP keepalive参数。net.ipv4.tcp_keepalive_time和tcp_keepalive_intvl设置太短会导致连接被误杀太长了断连又发现得慢。RabbitMQ官方推荐值是keepalive时间60秒间隔10秒重试次数3次。这个在跨网段的客户端连接场景下特别重要我遇到过因为云主机默认keepalive太长客户端死掉之后连接迟迟不释放数据库连接数被撑爆的。5.3 集群安装的关键准备事项虽然这篇讲的是单机安装但既然生产上用RabbitMQ很少有人永远跑单机简单说几个集群安装的硬前提。Erlang集群节点间通信依赖一个叫.erlang.cookie的密钥文件必须保证集群里每个节点的cookie一致。这个文件一般在/var/lib/rabbitmq/.erlang.cookie权限要求是只允许属主读写。还有节点名要规范。默认节点名是rabbithostname集群部署时建议设置RABBITMQ_NODENAME比如rabbit1mq-node01。每个节点的hostname都要能互相解析。一个常见的坑是在云环境下hostname是localhost.localdomain所有节点看起来都一样集群直接建不起来。解决方案是编辑/etc/hostname改成唯一的主机名再配合/etc/hosts让节点间能互相ping通。集群相关配置如果展开讲能写好几篇长文这里只是提醒你装单机的时候把主机名规划好以后集群就不用推倒重来。5.4 端口规划与安全组配置生产环境千万不要把RabbitMQ端口直接暴露到公网。RabbitMQ本身支持Username/Password认证但这不等于你能把它裸奔在公网上暴力破解、消息篡改的风险都很大。我在云上部署时光安全组就针对出问题反复调整过最终固定下来的是这套规则端口协议用途开放范围4369TCPepmd端口映射仅集群节点间5672TCPAMQP客户端仅业务网段15672TCPWeb管理界面仅运维网段25672TCP集群通信仅集群节点间如果要让RabbitMQ只监听内网IP在rabbitmq.conf里显式绑定listeners.tcp.local 172.16.0.10:5672 management.tcp.local 172.16.0.10:15672这样配置后外网连RabbitMQ就无从谈起了。安全这关过了再来谈性能和功能才有意义。6. 常见故障排查实录6.1 启动失败的第一排查动作RabbitMQ启动失败时别急着反复重启。第一步永远是看日志。日志默认路径在/var/log/rabbitmq/还有个更快的方式journalctl -u rabbitmq-server -n 100 --no-pager如果是二进制包启动可以直接加RABBITMQ_LOG_BASE环境变量指定日志目录。我见过最多的启动失败原因按出现频率排序是这四种Erlang版本不兼容、hostname无法解析、端口被占用、.erlang.cookie权限不对。每种都能在日志里找到明确关键词比如{error_logger, ...}段看到no such file or directory、connection attempt from disallowed node等那就直接定位到了。6.2 epmd与端口占用问题RabbitMQ启动完你以为万事大吉结果客户端连不上。这时候先确认epmd状态epmd -names如果输出里没有rabbit这个名字的条目说明节点没有正确注册到epmd。如果端口占用会看到Address already in use报错。排查时我会按顺序执行以下三条命令ss -lntp | grep -E 4369|5672|15672|25672 ps aux | grep beam journalctl -u rabbitmq-server -n 50beam.smp是Erlang虚拟机的主进程如果它在但端口没监听多半是配置有问题或者节点启动不完整。如果它根本没起来就看日志是哪个地方断的。我因为曾经在服务器上跑着其他服务占用了5672端口导致AMQP监听失败但管理端口正常这种情况排查快很多直接改配置端口或者停掉占用服务就行。6.3 连接被关闭clean channel shutdown 与常见回复码很多人第一次跑生产流量时会遇到一个很头疼的问题客户端日志里出现connection is closed随后跟着类似clean channel shutdown; protocol method: #method...(reply-code404, reply-textNOT_FOUND)的报错。这个clean channel shutdown其实不是TCP层面的暴力断开而是RabbitMQ在协议层主动关闭了通道原因就藏在该请求的reply-code里。我把常见的几个code整理出来了reply-code含义常见触发场景404NOT_FOUNDvhost、exchange、queue不存在405RESOURCE_LOCKED队列被其他连接独占406PRECONDITION_FAILED重复声明已存在但参数不同的queue/exchange530NOT_ALLOWED账号没权限或guest远程登录541INTERNAL_ERROR服务端内部错误需要查日志406是我碰到最多次的。原因通常是代码里声明了durabletrue的队列第二次启动参数写成了durablefalse或者同一个队列声明时arguments不一致。搜一下rabbitmqctl list_queues name durable就能对比出来只要把声明参数改一致问题就消失。530则通常是guest远程访问被拒按上面4.3节创建专用账号即可。6.4 内存和磁盘告警导致节点阻塞还有一种“假故障”特别容易误判服务进程还在端口还开着客户端却发不出消息或者消息一直堆积不消费。这时候看rabbitmqctl status的输出重点看alarms字段。如果里面有resource_limits或memory信息说明RabbitMQ触发了自我保护机制主动暂停接收新消息。这个机制设计的初衷是防止OOM但它也会给业务造成“服务不可用”的假象。排查思路是看当前内存和磁盘占用rabbitmqctl status | grep -A 20 alarms free -h df -h如果是内存告警除了升级服务器内存就是调vm_memory_high_watermark.relative。如果是磁盘告警RabbitMQ默认硬盘剩余低于1GB就会阻塞所有队列注意把disk_free_limit.absolute设置得比你的业务数据增长速度快一点。比如日志量大的场景这个值直接给到5GB都不为过。另外还有一种我踩过的细节Erlang的磁盘空间检测有滞后性尽量不要让RabbitMQ的数据目录和日志目录放到同一个磁盘分区否则日志占满盘RabbitMQ会直接罢工。6.5 常见问题速查表最后把日常运维中最高频的问题汇总成快查表方便你对照处理现象直接原因解决方案服务起不来日志提示Erlang版本不支持Erlang版本不匹配按官方矩阵安装支持的Erlang版本guest远程连接失败默认限制创建管理员账号或修改loopback_users队列消息一直堆积不消费内存/磁盘告警调高水位阈值或扩容连接正常但发送消息报NOT_FOUNDvhost或queue不存在rabbitmqctl list_vhosts和list_queues核对发送消息报PRECONDITION_FAILED重复声明参数不一致改为统一参数声明或删除旧队列重建重启服务器后服务丢了未设置开机自启systemctl enable rabbitmq-server高并发时连接被断开文件句柄不够调大nofile并重启服务集群节点互相连不上cookie或hostname问题统一cookiehostname必须互相解析这些坑都是真实环境中反复出现的原因不复杂只是不容易第一时间想到。我建议你把这一页收藏下来真遇到问题时对着表格一项一项排查比自己从零瞎猜能快很多。装RabbitMQ这件事说白了就三句话Erlang版本选对主机名解析做好端口和权限管清楚。剩下的都是“遇山开山”按日志一步步走就行。我个人最后一次装完习惯性跑一遍rabbitmqctl status、rabbitmq-plugins list、rabbitmqctl list_queues三个命令确认节点、插件、队列三层都正常才敢说“装好了”。你在实践过程中如果也遇到什么冷门的启动报错欢迎按这篇的排查思路先走一遍八成问题都出在这几个地方。
返回列表