整理:由OpenAI GPT-5(Codex)发布。
问题
线上 RabbitMQ 突然出现内存告警,
rabbitmq-diagnostics status 显示:机器有 30 GiB 内存,当时操作系统仍有约 16 GiB available,但是 RabbitMQ 的内存高水位配置为 1.6106 GB。
这里的
vm_memory_high_watermark 是 RabbitMQ 的内存告警阈值。进程内存超过该值后会触发 Memory Alarm,并对消息发布端进行流控。它可以配置为相对值,也可以直接配置绝对值:本次节点使用
rss 作为计算策略,因此告警判断主要看 RabbitMQ 进程的常驻内存。官方说明可以参考 Memory Threshold and Limit。排查
首先查看 RabbitMQ 的内存分类:
其中主要数据为:
binary 是 Erlang Runtime 的 binary heap 分类,通常会包含消息体、属性和网络数据相关的二进制对象;allocated_unused 是 Runtime 已经分配但当前没有使用的内存;连接、队列、消息索引和管理插件则会分别统计在其他分类中。各分类含义可以参考 RabbitMQ 官方的 Reasoning About Memory Use。继续使用
recon_alloc 查看 Erlang allocator:得到的主要结果是:
allocator 数据是 Erlang 内存分配器管理的 carrier 和 block 口径,RabbitMQ 当前告警使用的是 RSS 口径,所以继续查看进程实际驻留内存:
结果为:
VmRSS 大约为 1.59 GiB,与 RabbitMQ 上报的 Total memory used 基本对应,并且已经超过 1.5 GiB 左右的高水位,所以 Memory Alarm 的触发是符合当前配置的。接着看 memory breakdown 中的队列相关数据,
queue_procs 只有几 MB,msg_index 也很低,没有体现出同等量级的队列内存。此时连接数为 1143,Socket 数为 1143,连接数量更值得继续拆分。使用下面的命令按客户端来源聚合连接:
结果显示,某一个脱敏后的内网来源持有 993 条连接,其他来源大多数只有个位数或十位数。再查看连接详情:
根据来源地址定位到对应的数据服务。重启该服务后,RabbitMQ 上来自该来源的连接数量明显减少,同时内存也开始回落,因此可以确认这些连接由该服务持有。
但是重启只能确认连接归属,接下来还需要看为什么连接会一直增加。RabbitMQ Java Client 中 Connection 是物理 TCP 连接,Channel 是复用在 Connection 上的逻辑通道。官方建议 Connection 和 Channel 都应该长时间复用,相关生命周期说明见 Java Client API Guide。
总结
这次排查主要由下面几条证据串起来:
- RabbitMQ 使用 RSS 计算内存,进程
VmRSS已经超过vm_memory_high_watermark。
- 队列进程和消息索引占用较低,没有对应规模的队列内存增长。
- 1143 条连接中,有 993 条集中来自同一个客户端来源。
- 重启对应服务后,该来源连接数和 RabbitMQ 内存同步下降。
- 两个消费者每分钟执行一次恢复,八小时理论新增约 960 条连接,与现场数量基本一致。
- 代码调用链中存在周期性创建新 Connection、但旧 Connection 生命周期不完整的路径。
最终定位为客户端 Connection 持续累积导致 RabbitMQ RSS 上涨,并在超过配置的内存高水位后触发告警。