消费者刚启动只占 180 MB,运行几小时后增长到 1.5 GB,最后被 OOM Killer 终止。把 PHP memory_limit 调大只会推迟故障。长进程中,静态缓存、批处理数组、未释放实体和第三方 SDK 连接会跨消息保留。

判断是预热平台还是线性增长

pgrep -af 'queue:consumers:start'
pid=12345
while true; do
 date '+%F %T'
 ps -o pid,rss,vsz,etime,cmd -p "$pid"
 sleep 30
done

启动后增长一段再稳定,可能是框架预热;每处理固定数量消息都增加相近 RSS,更像业务代码保留引用。把消息处理数与 RSS 放在同一时间轴上,不要只截一张 top。

用 max-messages 设置生命周期

bin/magento queue:consumers:start async.operations.all --max-messages=500
bin/magento queue:consumers:start product_action_attribute.update --max-messages=200

让 Supervisor 在进程正常退出后重启。max-messages 是保护边界,不是修复内存泄漏;值要根据单条消息耗时、启动成本和内存曲线测定。

[program:magento-consumer]
command=/usr/bin/php /var/www/html/bin/magento queue:consumers:start async.operations.all --max-messages=500
autostart=true
autorestart=true
stopsignal=TERM
stopwaitsecs=60
numprocs=1
redirect_stderr=true

最常见的累积点

  • 把每条消息实体追加到类属性数组;
  • 循环加载完整 Product/Order 对象并长期持有;
  • 失败后保留异常对象和大响应正文;
  • 图片、XML 或 CSV 资源没有在 finally 中关闭;
  • 第三方 SDK 静态缓存没有 reset。
journalctl -k --since '2 hours ago' | grep -iE 'oom|killed process'
grep -RniE 'Allowed memory size|out of memory' var/log | tail -n 50
bin/magento queue:consumers:list

上线后同时观察队列积压、每分钟处理量、失败重试和 RSS。内存稳定但队列越来越长,说明重启间隔过于保守;内存下降而重复消费增加,则检查消息确认和幂等性。最终目标是吞吐、内存与正确性一起稳定。

给退出留出确认消息的时间

Supervisor 的 stopwaitsecs 要长于最慢一条消息的正常处理时间。过早发送 KILL 会让消息来不及 ack,进程重启后再次消费。发布时先发 TERM,观察当前消息完成、连接关闭和进程退出,再由进程管理器启动新实例。

每次重启前后记录进程退出码;正常达到 max-messages 应是可预期退出,不能和 OOM、SIGKILL 混在同一 autorestart 指标里。