ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

RabbitMQ 安装避坑指南:从版本选型到集群扩展

RabbitMQ 安装避坑指南:从版本选型到集群扩展 每次帮人排查 RabbitMQ 安装问题我听到最多的一句话就是我明明是照着教程敲的命令怎么最后还是起不来 这种场景太熟悉了——要么是 Erlang 版本和 RabbitMQ 对不上要么是防火墙忘了放行端口要么是管理界面死活打不开。作为一个在 Linux 服务器上反复折腾过 RabbitMQ 的老运维我打算把整个安装过程拆开揉碎了讲一遍从版本选型到集群扩展把该避的坑全部摆在明面上。这篇文章适合谁看刚接触消息队列的开发者、需要在 CentOS 或 Ubuntu 上快速搭建 RabbitMQ 环境的运维人员以及那些想搞清楚为什么我装完不能用的排错者。我会用最直白的话把原理讲清楚给你可以直接复制执行的命令也会把那些文档里不会写、只有踩过坑才知道的细节全部交代出来。1. 安装前必须想清楚的三件事1.1 一句话说清 RabbitMQ 到底解决什么问题很多人在搜索引擎里输入什么场景会用到RabbitMQ一句话讲清楚说明大家对消息队列的定位还是有些模糊。直白讲RabbitMQ 就是一个中间人——它站在生产者Producer和消费者Consumer之间负责把消息安全地送达到目的地。生产者不直接调用消费者的接口而是把消息扔进队列里消费者根据自己的处理能力按需取走。举个具体例子你的订单系统在用户下单后需要发送邮件、短信、站内信还要更新库存、生成日志。如果所有这些操作都在请求线程里同步执行高峰期一个下单请求可能耗时好几秒用户早就等得不耐烦了。引入 RabbitMQ 之后订单系统只需要把下单成功这个事件丢进交换机后续的邮件服务、短信服务各自去队列里取消息互不干扰下单请求的响应时间直接降到毫秒级。这就是典型的异步解耦也是消息队列在企业级应用中最核心的价值。1.2 版本选型直接决定你能不能正常启动RabbitMQ 对 Erlang 版本有严格的要求这是安装过程中最容易翻车的环节。很多新手图省事直接在系统里装了一个最新版的 Erlang结果 RabbitMQ 启动时直接报错退出日志里全是类似Failed to start RanchListener或者{:bad_return, {{:error, {:function_clause, ...这类让人头皮发麻的信息。我在生产环境首选的组合是 CentOS 7 Erlang 25.3.2.8 RabbitMQ 3.12.x这一套已经跑了好几年非常稳。如果你用的是 Ubuntu 20.04 或 22.04也可以尝试 Erlang 24.x 加 RabbitMQ 3.10.x 的搭配。不建议盲目追求最新版本因为在开源社区里新的主版本往往意味着配置格式调整和插件兼容性变化刚发布的前几个月最容易踩到坑。RabbitMQ 官方维护了一份 Erlang 版本兼容性表格URL 是https://www.rabbitmq.com/which-erlang.html在你锁定版本之前务必先去这张表上确认一下你的 Erlang 版本是否在支持范围内。千万别凭感觉来版本不对后面的一切都白搭。为了方便你查阅我把常见的 RabbitMQ 版本与 Erlang 版本对应关系整理成了表格RabbitMQ 版本推荐的 Erlang 版本最低 Erlang 版本备注3.12.x25.x25.0目前最稳定的选择之一推荐生产环境使用3.11.x25.x 或 24.324.3兼容性较好老项目常用3.10.x24.3 或 23.223.2对 Erlang 版本要求比较宽松3.9.x23.2 或 24.x23.2较老版本仍可满足部分旧系统需求3.8.x22.x 或 23.x22.0非常老的版本建议不再新装1.3 基础依赖缺失隐患装完报错找不到关键命令还有一个容易忽略的点是主机名设置。RabbitMQ 有用到 hostname 来做节点间通信如果你的 /etc/hosts 文件里没有配置本机的 hostname 映射启动会有警告日志。这条警告一般不影响单机运行但如果你后面要搭集群就是妥妥的隐患。我建议在安装之前先执行hostnamectl set-hostname rabbitmq-node1把主机名固定下来再往 /etc/hosts 里加上一条映射记录格式类似192.168.1.10 rabbitmq-node1。这样能避免后续节点间通信因为主机名解析失败而出各种莫名其妙的状况。2. 一步一步完成 RabbitMQ 的安装2.1 提前安装必要的底层依赖无论你用的是 CentOS 还是 Ubuntu先执行系统更新并安装基础依赖包总没错。在 CentOS 上我的习惯是这样yum update -y yum install -y epel-release vim net-tools wget lsofEPEL 源非常重要因为 RabbitMQ 后续需要的一些依赖工具可能只存在于 EPEL 中。net-tools提供 netstat 命令后面排查端口监听情况时离不开它lsof则用于查看文件占用情况比如你怀疑某个端口被别的进程占用了一条lsof -i:5672就能看明白。如果你用的是 Ubuntu命令对应改成apt update -y apt install -y curl wget vim net-tools lsof2.2 安装 Erlang 的两种方式对比RabbitMQ 是跑在 Erlang 虚拟机上的所以第一步必须先装 Erlang。我尝试过两种方式一种是用系统自带的软件源直接安装另一种是去 Erlang Solutions 官网下载预编译包。系统自带源省事但版本往往偏旧比如 CentOS 7 自带源里的 Erlang 还停留在 20.x而 RabbitMQ 3.12 需要 Erlang 25这中间差了十万八千里。所以我更推荐用erlang-solutions仓库来安装wget https://packages.erlang-solutions.com/erlang/rpm/centos/erlang-solutions-2.0-1.noarch.rpm rpm -Uvh erlang-solutions-2.0-1.noarch.rpm yum install -y erlang装完后执行erl -version确认版本。如果显示的是 25.x 或更高就说明 Erlang 没问题了。这里要多说一句Debian/Ubuntu 系统上对应的安装方式是在官网下载 deb 包或者配置 apt 源。不管用什么发行版核心思路都一样确保 Erlang 版本满足 RabbitMQ 的依赖要求别偷懒用老到掉牙的版本。2.3 下载并安装 RabbitMQ ServerErlang 就绪后接下来就轮到 RabbitMQ 本体了。RabbitMQ 官方对 CentOS 提供了编译好的 rpm 包也提供了通用的 tar.xz 压缩包。生产环境我更推荐 rpm 包原因很简单它自带 init 脚本安装完成后可以直接用 systemd 管理省去了手工配置服务的麻烦。wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.12.14/rabbitmq-server-3.12.14-1.el8.noarch.rpm yum install -y rabbitmq-server-3.12.14-1.el8.noarch.rpm注意我安装时用的是 el8 的包但它在 CentOS 7 上同样可以正常工作。曾经有人纠结过这个问题其实 RabbitMQ 的 rpm 包对 CentOS 7 和 8 的兼容性做得相当好只要依赖满足就能装上。装完之后你可以在/usr/lib/rabbitmq/lib/rabbitmq_server-3.12.14/目录下看到完整的 RabbitMQ 安装目录里面包含了sbin目录下的各类管理脚本。接下来要安装 RabbitMQ 的依赖包socat这是一个用于数据转发的工具RabbitMQ 启动时会依赖它yum install -y socat不装这个包去直接启动 RabbitMQ大概率会报错提示缺少某个动态库或命令。所以顺序不要乱先装好依赖再启动。2.4 启动服务和设置开机自启很多人在这一步会忽略一个关键点RabbitMQ 启动需要一定时间特别是第一次启动它要初始化数据库和生成各种配置文件。如果启动后马上执行状态查询得到的结果可能是节点未运行。给系统几秒钟时间然后再确认状态。systemctl start rabbitmq-server systemctl enable rabbitmq-server systemctl status rabbitmq-serversystemctl enable这行命令必不可少否则服务器重启后 RabbitMQ 不会自动拉起。生产环境可不会有人每次都手动上去敲启动命令开机自启是底线要求。如果你启用了防火墙还需要放行 RabbitMQ 相关的端口firewall-cmd --permanent --add-port5672/tcp firewall-cmd --permanent --add-port15672/tcp firewall-cmd --reload5672 是 AMQP 协议端口客户端连接就用它15672 是 Web 管理界面的端口。这两个端口在后面测试和日常管理时都会用到提前放行能省掉后续很多麻烦。3. 创建管理员账号并开启远程访问3.1 理解 guest 账号的限制RabbitMQ 安装完成后默认会创建一个guest账号密码也是guest。但这里有一个隐藏的坑guest账号只允许从 localhost 连接如果你试图从局域网内的其他机器通过该账号连接连接会被直接拒绝日志里出现类似user guest can only connect via localhost的提示。这是 RabbitMQ 出于安全考虑故意设计的约束生产环境中我们不应该去突破这条限制而是应该创建一个专门用于远程访问的管理员账户。3.2 操作步骤与管理节点下面是创建管理员账号的核心步骤我建议直接复制下来执行rabbitmqctl add_user admin your_password_here rabbitmqctl set_user_tags admin administrator rabbitmqctl set_permissions -p / admin .* .* .*第一条命令创建用户并设置密码第二条命令把这个用户的标签设置为administrator赋予其管理权限第三条命令授予该用户在默认虚拟主机/上的全部读写权限。这里的.*分别代表了 configure、write、read 三个维度的权限匹配规则.*表示匹配所有资源。完成以上步骤后可以通过以下命令验证rabbitmqctl list_users你会看到admin用户已经被添加并且带有administrator标签。到这里远程访问的权限已经准备就绪。3.3 开启 Web 管理界面默认情况下 Web 管理界面插件是未启用的你需要手动开启。开这个插件只需要一条命令rabbitmq-plugins enable rabbitmq_management执行完会提示插件已启用并且输出一段说明Web 管理界面默认监听在 15672 端口。这个插件是纯 Erlang 写的启用的过程本质上是告诉 RabbitMQ 在启动时加载该插件对应的应用模块。顺带一提RabbitMQ 提供了一堆有用的插件常见的还有rabbitmq_consistent_hash_exchange一致性哈希交换机和rabbitmq_delayed_message_exchange延迟消息交换机这些在业务上经常用到。插件启用后在浏览器访问http://你的服务器IP:15672用刚才创建的admin账号登录就能看到管理界面了。从这一步开始你会发现 RabbitMQ 的很多操作其实都可以在 Web 界面上完成比如创建交换机、声明队列、查看连接状态等。对于那些不喜欢敲命令的新手来说图形界面确实友好很多。不过我还是要劝你命令行方式才是排查问题的最强武器尤其在管理界面都打不开的时候。4. Web 管理界面里值得关注的核心功能Web 管理界面上线之后很多人都只是用它看看队列有没有堆积实际上这个界面的信息量非常大。左侧导航栏里的Overview、Connections、Channels、Exchanges、Queues每一个标签都对应着运行时状态的一个维度。在Overview页面你能看到全局的消息处理速率、连接数、通道数还能看到当前节点的内存和磁盘使用情况。我曾经历过一次线上事故消息堆积达到了几百万条消费端怎么都消化不掉当时就是靠这个页面发现内存和磁盘接近阈值才及时扩容的。界面上有实时的图表刷新值班人员哪怕只是偶尔看一眼也能第一时间感知到异常。Queues页面则是排查消费问题的第一现场。你可以直接在界面上查看某个队列的Ready消息数和Unacked消息数。如果Ready数量持续上涨说明消费者拉取消息的速度跟不上生产速度如果Unacked居高不下说明消费者可能卡在某个消息处理中没有正常 ack。这两种情况对应的调优方向完全不同不看数据就瞎猜往往事倍功半。还有一个容易被忽略的功能是Exchanges页面的消息发布测试。你可以在界面上手动往指定的交换机发布一条 JSON 消息然后去队列里查看是否被路由进来。这个操作在排查路由键配置问题时特别管用可以快速判断问题出在交换机绑定上还是消费者逻辑上。5. 用 systemd 管理 RabbitMQ 服务RabbitMQ 安装后默认集成到 systemd 中正常情况下你直接用systemctl系列命令操作即可。说了这么多有些同学可能还不知道怎么快速查看 RabbitMQ 的运行日志。RabbitMQ 的 node 日志默认位于/var/log/rabbitmq/启动失败时第一件事应该是去翻这个目录下的日志文件而不是重新启动一遍再干瞪眼。tail -f /var/log/rabbitmq/rabbityour_hostname.log这个日志文件会详细记录 RabbitMQ 启动过程的每一个阶段。如果你看到日志里出现 Erlang Crash Dump 相关信息说明 Erlang 虚拟机本身就崩了很大概率是版本兼容问题或者内存资源不足。如果看到的是某个插件加载失败的堆栈那你就知道该去排查插件版本与 RabbitMQ 版本是否一致。在 systemd 的管理体系下服务配置文件生成在/usr/lib/systemd/system/rabbitmq-server.service。这个文件里定义了启动用户、启动命令、环境变量等关键字段。除非你知道自己在做什么否则不建议修改这个文件里的内容因为它被包管理器管理升级 RabbitMQ 时可能会被覆盖。如果你需要自定义一些高级配置比如修改内核参数、提高文件描述符限制更推荐的做法是在/etc/rabbitmq/rabbitmq.conf里配置这个文件是 RabbitMQ 自身的配置文件升级不会被覆盖。6. 我整理的高频问题与排查思路6.1 Erlang 版本不匹配导致启动失败症状systemctl start rabbitmq-server执行后服务状态一会儿 active 一会儿 failed或者日志里出现含{:error, {:function_clause, ...}}这样的 Erlang 函数子句错误。排查方法首先确认 Erlang 版本执行erl -version然后对照前文给出的兼容表看看你的版本是否在支持范围内。如果版本差异过大最直接的办法是卸载当前 Erlang重装兼容版本。另外执行erl进入交互模式后可以查看运行时版本信息。我见过最离谱的一次情况是服务器上有两个版本的 Erlang 共存环境变量指向了旧版本导致 RabbitMQ 怎么都启不起来。后来用which erl才发现问题所在于是调整 PATH 环境变量把新版本路径放在前面问题立刻解决。所以遇到启动异常第一步先查版本和路径这个过程最多花五分钟。6.2 内存高水位警报导致队列阻塞症状生产端不断往队列发送消息但消费者数量为 0Web 管理页面上能看到类似Memory high watermark exceeded的红色告警。原因RabbitMQ 有内存阈值保护机制默认阈值为节点可用内存的 40%。当内存占用超过这个阈值RabbitMQ 会阻塞所有连接的生产端不再接收新的消息直到内存占用降下来。这是保护机制在起作用不是故障但如果你没有及时感知会造成消息堆积和大面积的生产超时。解决思路通过rabbitmqctl set_vm_memory_high_watermark 0.6可以把阈值调高到 60%。但我不建议在未经过压测的情况下随意调高因为暴露内存不够的本质问题是流量增长太快正确的操作应该是扩容节点或在消费者侧加快消息处理速度。调阈值只是治标不治本。6.3 端口不通客户端连不上症状客户端通过host: 5672连接时超时或者提示 Connection refused。排查步骤netstat -tlnp | grep 5672这个命令能立刻告诉你 5672 端口有没有处于监听状态。如果没有输出说明 RabbitMQ 进程没有正常监听端口需要回去看服务状态日志。如果端口在监听但客户端还是连不上那就是防火墙拦截了流量放行规则在前文已经写过。这里有个细节RabbitMQ 默认监听在 0.0.0.0 上还是 127.0.0.1 上取决于配置。如果你发现它只监听了 127.0.0.1说明配置被改过。你需要检查/etc/rabbitmq/rabbitmq.conf的listeners.tcp.default配置项。6.4 目录权限导致的启动崩溃症状启动日志中出现cannot create directory或permission denied的报错。RabbitMQ 默认把所有运行数据存放在/var/lib/rabbitmq/mnesia目录下这个目录归rabbitmq用户所有。有时候你手头紧贴拷贝了旧的数据目录但忘了修改属主和属组启动时就会因为权限不足而崩溃。修复方法很简单chown -R rabbitmq:rabbitmq /var/lib/rabbitmq/这个操作我在服务器迁移时做过很多次每次都能解决因为权限问题导致的启动异常。6.5 防火墙外的另一重障碍SELinux在 CentOS 默认开启 SELinux 的情况下即使防火墙端口放行了SELinux 也可能阻止 RabbitMQ 绑定端口或访问某些文件。如果日志里没有任何明显报错服务就是起不来你值得去检查一下 SELinux 的审计日志。最省事但又相对安全的做法是只关闭针对 RabbitMQ 相关进程的 SELinux 限制而不是全局禁用 SELinux。对于多数测试环境和部分内网环境直接把 SELinux 设为 permissive 模式也能让 RabbitMQ 正常工作setenforce 0把SELINUXenforcing改为SELINUXpermissive写在/etc/selinux/config里后需重启系统才生效。生产环境如果安全要求很高建议用audit2allow工具生成对应的 SELinux 策略模块妥善放行 RabbitMQ 所需的能力但这属于进阶运维操作这里先不展开。7. 从单机走向集群你需要提前知道的扩展方向RabbitMQ 安装好之后只是起点生产环境迟早要面临单点故障和高并发压力。RabbitMQ 支持集群部署模式多个节点组成一个逻辑集群共享虚拟主机、交换机和队列状态。集群中的节点之间通过 Erlang 分布式协议通信这个通信依赖同一个 Erlang cookie。搭建集群前你需要把集群中所有节点的 Erlang cookie 保持一致。默认路径是/var/lib/rabbitmq/.erlang.cookie这个文件权限必须设置为仅属主可读写chmod 400 或 600否则 RabbitMQ 会认为 cookies 不完整而拒绝通信。具体操作用scp把 cookie 文件从第一个节点复制到其他节点然后重启各节点的 RabbitMQ 服务。加入集群的命令非常简单在第二个节点上执行rabbitmqctl stop_app rabbitmqctl join_cluster rabbitrabbitmq-node1 rabbitmqctl start_app这样第二个节点就成了集群的一部分。但要实现真正的高可用你还需要配置镜像队列或 Quorum 队列才能保证某个节点挂了队列数据不丢、服务不中断。这些内容展开讲又是一篇文章这里先点到为止等你的单机跑通之后再深入研究也不迟。8. 折腾这么多年我总结的几条实操心得最后说几条我个人的土办法可能不太教科书但都经过线上环境验证过。第一不要把 Erlang 自动更新开关打开。Erlang 的 minor 版本升级有时会改变内部行为导致运行中的 RabbitMQ 节点悄悄异常。每次升级 Erlang 前我会先停掉 RabbitMQ备份好/var/lib/rabbitmq/mnesia目录再升级升级完启动后立刻观察日志。图省事直接在运行中升级 Erlang风险多到数不清。第二管理界面上的图表不要关了就不管。我的习惯是在运维监控系统里把 RabbitMQ 的队列消息数、连接数、通道数、内存占用这几个指标都接入告警一旦 Ready 消息数量超过预设阈值马上通知值班人员。等用户报障再来看队列堆积事情通常已经闹大了。第三别忘了 RabbitMQ 的 AutoDelete 策略。测试环境亲手创建的临时队列如果不设置auto_delete它们会永远留在服务器上不断累积。每隔一段时间用rabbitmqctl list_queues扫一遍清理掉那些长时间没有消费者且消息数为 0 的垃圾队列让服务器保持清爽。第四面对启动失败这类问题时不要反复执行systemctl restart。连续重启只会让日志里多几行一模一样的记录对排查毫无帮助。先翻日志、再查版本、再查端口、再查权限按这个顺序来大部分问题十分钟之内能定位。RabbitMQ 的安装真的不难难的是理解它的运行机制和数据保护策略。只要你严格按照规范操作把基础打好后面无论是做业务开发还是日常运维都会顺很多。希望这篇东西能帮你省下几个小时的折腾时间有没说到的细节也欢迎在评论区里一起聊。
返回列表