Consumer 刚启动只有 120MB,跑两个小时后接近 2GB,最终被容器 OOMKilled。Supervisor 重启后又重复。把 memory_limit 调大只会延后死亡,首先要确认内存是随“消息数量”线性增长,还是被某一条异常消息瞬间撑爆。
给内存曲线加上消息坐标
在不记录敏感 payload 的前提下,记录 consumer 名称、消息 ID、topic、处理耗时、payload 字节数、处理前后 memory_get_usage(true) 与峰值。这样能区分:
- 每处理一条都增加少量且不回落:对象或静态缓存跨消息残留;
- 某个 SKU/订单后突然跳高:特定业务数据导致大集合;
- 失败消息快速重回队列:重试风暴不断创建异常和日志;
- 空闲时也增长:连接、监控或框架循环存在问题。
同时查看容器和宿主机指标,不要把 PHP memory_limit 异常与系统 OOM 混为一谈。
长生命周期进程和普通 Web 请求不一样
Web 请求结束后对象整体释放,Consumer 会在同一进程处理很多消息。单例服务、Registry、静态数组、第三方 SDK 客户端缓存,如果没有每条消息重置,就会长期保留引用。批量加载 collection 后也应及时释放大数组,避免把完整实体堆进后续闭包。
用固定的消息样本在预发布循环重放,配合 PHP profiler 或周期性内存快照定位增长点。不要在生产直接打印完整对象图,它可能包含客户信息,也会额外放大日志与内存。
先用有限生命周期止血,但别把它当根治
Magento consumer 支持限制处理消息数,进程管理器可在正常退出后重启。合理的 max_messages 能降低长期碎片和不可控增长,是保护措施;如果每十条消息就需要重启,仍说明代码或数据有问题。
php bin/magento queue:consumers:start consumer.name --max-messages=1000重试也要有上限和延迟。永久失败的消息应进入明确的失败处理流程,而不是立即 nack/requeue 形成热循环。记录失败次数和最后异常,确保同一消息不会无限消耗资源。
修复后的验证方式
用小消息、正常大订单、故意失败消息三组样本,各跑足够数量。内存可以在启动阶段上升,但随后应进入稳定区间;队列吞吐不下降;失败消息按设计停止重试;进程被重启时没有未确认消息长期悬挂。只有曲线、吞吐和业务结果都稳定,才算解决。

