RabbitMQ 在运行过程中如果突然不再接收生产者发送的消息,客户端连接被标记为 blocked,管理界面上出现红色的内存告警提示,这往往意味着节点触发了内存资源限制。很多初次遇到这类问题的同学会误以为是网络故障或者客户端 Bug,实际上这是 RabbitMQ 内置的自我保护机制在起作用。理解这套机制的运作方式,才能在故障发生时快速定位根因并做出正确的处置。

一、内存告警与阻塞连接的工作原理
RabbitMQ 启动时会检测当前系统的总内存,并将其乘以一个比例系数得到内存阈值,默认比例是 0.6,也就是说默认情况下当 RabbitMQ 使用的内存达到系统总内存的 60% 时,就会触发内存告警。触发告警后,节点会阻塞所有支持阻塞功能的发布连接,生产者发送的消息不再被接收,直到内存降回阈值以下才会解除阻塞。需要注意,消费者仍然可以正常消费消息,这正是排查时的一个重要线索:如果生产阻塞而消费正常,大概率就是内存告警。
计算总内存时 RabbitMQ 会优先参考 cgroup 的限制。如果节点跑在容器里,且容器设置了内存上限,那么阈值是基于容器上限计算的,而不是宿主机物理内存。这一点经常造成误解:宿主机明明还有大量空闲内存,容器里的 RabbitMQ 却触发了告警。查看当前阈值可以直接执行命令:
# 查看内存阈值相关信息
rabbitmqctl status | grep -A 5 "Memory"
# 查看当前的高水位线比例(返回示例:{"vm_memory_high_watermark",0.6})
rabbitmqctl eval 'vm_memory_monitor:get_memory_use(current).' | head -5
除了全局阈值,还有一个配套参数 vm_memory_high_watermark_paging_ratio,默认为 0.5。当内存使用达到阈值的 50% 时,RabbitMQ 会先把持久化消息尽快写入磁盘以释放内存,尽量推迟真正阻塞的到来。如果这个比例设置不合理,或者消息本身不可分页,阻塞仍然可能发生。阻塞发生后,客户端如果启用了 publisher confirm,会观察到 confirm 一直不返回;未启用 confirm 的客户端则可能表现为发送缓冲区堆积,这是排查时可以从客户端侧观察到的典型现象。
二、流控机制的触发条件与表现
内存告警之外,RabbitMQ 还有一套基于信用流的流控机制。每个连接在 RabbitMQ 内部由一个进程处理,进程之间通过信用额度协调收发速度。当某个通道的消息发布速度过快,超过 RabbitMQ 内部处理能力时,连接会被临时限制,客户端表现为发送出现停顿。这种流控通常是短暂的、正常的自我调节,不需要干预;但如果停顿频繁且持续时间长,说明后端处理能力确实跟不上,需要排查消费端。
流控与内存阻塞在现象上的区别很重要:内存阻塞是全局性的、持续性的,所有发布连接一起停;信用流控通常只影响单个高吞吐连接,而且是间歇性的。在管理控制台的 Connections 页面,state 列显示为 blocked 或 blocking 可以帮助区分。blocked 表示该连接因为资源告警被阻塞,blocking 表示它正准备阻塞。结合服务端日志可以进一步确认:
# 实时查看内存告警相关日志 tail -f /var/log/rabbitmq/rabbit@node1.log | grep -i "memory" # 常见的日志关键字 # memory resource limit alarm set on node rabbit@node1 # memory resource limit alarm cleared on node rabbit@node1 # 查看哪些连接处于阻塞状态 rabbitmqctl list_connections name state send_pend
send_pend 列表示该连接待发送的数据量,如果这个值很大,说明客户端侧也在堆积。另外要留意 disk_free_limit,磁盘剩余空间低于该值同样会触发资源告警并阻塞发布,很多时候内存告警和磁盘告警是伴随出现的,排查时不要只盯着一处。
三、定位内存占用的真正元凶
触发告警只是表象,真正要回答的问题是内存被什么占用了。最直接的入口是管理控制台或命令行查看内存分布:
# 查看各类内存占用明细
rabbitmqctl status | grep -A 30 "Memory calculation strategy"
# 按进程内存排序,找出最重的 Erlang 进程
rabbitmqctl eval 'spawn(fun() -> io:format("~p~n", [recon:proc_count(memory, 20)]) end).'
内存分布报告中重点看几个部分:binary 占比过高通常意味着有大量消息体被缓存在内存中,常见原因是消息堆积在非惰性队列里;quorum queues、connections、management 等 H2 目录也各有归属。如果 binary 一项占了绝大部分,下一步就应该去看哪些队列的消息数异常。通过 rabbitmqctl list_queues name messages_ready messages_unramvacked consumers memory 可以一次性拿到所有队列的消息量、消费者数和内存占用,找出那些消息量巨大但 consumers 为 0 的队列,它们往往就是罪魁祸首。
消费者掉线导致堆积是最常见的根因。消费服务发布失败、消费逻辑抛异常反复 requeue、或者消费者根本没有配置自动重连,都会造成消息无限堆积。此外还有两类容易被忽视的情况:一是单条消息过大,几 MB 的消息批量进出会迅速拉高 binary 内存;三是大量未关闭的连接泄漏,僵尸连接积累同样消耗内存。针对大消息场景,建议从架构上改造,消息体存外部存储,MQ 只传递引用,这比单纯调大阈值健康得多。
四、治理方案与长期预防
应急恢复的思路是尽快让内存降下来:恢复消费者消费堆积消息是最根本的手段;必要时可以临时调高阈值为自己争取时间:
# 临时调高内存阈值到 0.7(重启后失效,谨慎使用) rabbitmqctl set_vm_memory_high_watermark absolute 4GB # 将堆积队列转为惰性队列(3.12+ 版本支持在线转换) rabbitmqctl transform_queue my_queue lazy # 优先处理磁盘告警 rabbitmqctl set_disk_free_limit 5GB
长期治理上,首先是队列选型。对于消息量大、可能长时间无人消费的队列,直接使用惰性队列或 quorum queue 的消息默认落盘特性,消息不常驻内存,从根本上消除堆积引发的内存问题。其次阈值规划要结合部署形态:物理机部署时 0.6 是合理的默认值,容器部署时要确认 limit 与阈值匹配,并显式设置 vm_memory_high_watermark,避免误读宿主机内存。若节点上还有其他服务共享内存,则应使用 absolute 形式给出明确的字节数。
监控层面,建议对以下指标建立告警:节点的 memory 使用量与阈值的比值、处于 blocked 状态的连接数、各队列的 messages_ready、消费者数量变化。内存到达阈值的 80% 就应该提前告警,而不是等到阻塞发生才发现。配置示例:
# rabbitmq.conf 关键配置 vm_memory_high_watermark.relative = 0.6 vm_memory_high_watermark_paging_ratio = 0.5 disk_free_limit.absolute = 2GB
最后梳理一下完整的排查路径:先确认告警类型(内存还是磁盘)→ 查看阻塞连接与日志时间线 → 分析内存分布定位到具体队列或连接 → 恢复消费或清理堆积 → 复盘消费端健壮性与阈值配置。按照这条链路走下来,绝大多数 RabbitMQ 内存告警问题都能在短时间内定位并解决,同时也能把同类问题堵在发生之前。
RabbitMQ内存告警RabbitMQ流控消息堆积排查修改时间:2026-09-02 22:45:11