vegeta 是唯一能稳定复现 frankenphp worker 内存阶梯式上涨的 cli 工具,需通过 -rate=50 -duration=5m 持续压测并结合 ps rss、memory_get_peak_usage、opcache_status 和 warning 日志综合诊断泄漏根源。

用 vegeta 发起持续请求并捕获内存峰值
FrankenPHP 的内存增长问题只在长周期、多请求下暴露,curl 或单次 wrk 压测根本抓不到。必须用支持连接复用、可导出原始响应的工具——vegeta 是目前唯一能稳定复现 worker 进程内存阶梯式上涨的 CLI 工具。
关键不是压高 QPS,而是让同一 worker 处理足够多请求,触发缓存累积或引用未释放:
- 写
targets.txt:内容为GET http://localhost:8000/api/health(确保是真实业务 endpoint,别用静态文件) - 运行命令:
vegeta attack -targets=targets.txt -rate=50 -duration=5m -http2 -include-body | vegeta report -type=json > result.json - 压测中同步执行:
watch -n 1 'ps aux --sort=-rss | grep frankenphp-worker | head -3',盯 RSS 列是否随时间单调上升
在控制器里打点验证内存是否真在涨
仅看 ps 的 RSS 不够准:它包含共享内存、mmap 映射等噪音。得进 PHP 进程内部看真实堆使用量,尤其要区分“正常缓存”和“泄漏”。
在 Laravel/Symfony 控制器或中间件里加一行日志:
error_log('PID: ' . getmypid() . ', peak: ' . memory_get_peak_usage(true) . ', total: ' . memory_get_usage(true));
然后用 tail -f storage/logs/laravel.log | grep PID 实时观察。重点看两个信号:
- 如果
peak每 10–20 个请求就跳一次(比如从 42MB → 48MB → 55MB),且不回落,大概率是 EntityManager 缓存未 clear 或闭包监听器强引用 - 如果
peak阶梯上升后某次请求突然归零(如 60MB → 3MB),说明触发了max_requests重启,不是泄漏,是配置太激进
用 opcache_get_status 和 gc_collect_cycles 辅助定位
内存增长常伴随 OPcache 失效或 GC 未及时介入。FrankenPHP worker 模式下,gc_collect_cycles() 不会自动触发,得手动干预。
在 kernel.terminate 或请求末尾加诊断逻辑:
if (random_int(1, 100) === 1) {
$op = opcache_get_status();
error_log('OPCache used: ' . $op['memory_usage']['used_memory'] . '/' . $op['memory_usage']['total_memory']);
gc_collect_cycles();
}
同时检查:opcache.memory_usage.used_memory 是否持续上涨却不释放;若 opcache_statistics.oom_restarts > 0,说明 OPcache 自身已因内存不足重启过,会拖慢整个 worker。
别忽略日志里高频出现的 warning 级别提示
某些看似无害的 warning,在 worker 长期运行中会悄悄吃掉内存:比如 Undefined array key 触发的错误处理栈积累、Trying to access array offset on value of type null 导致的临时 zval 泄漏。
在 php.ini 或 FrankenPHP 的 frankenphp.yaml 中设:error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT,再配合 log_errors = On,压测后用 grep -c "PHP Warning" /var/log/php-error.log 统计频次。
若每千请求出现超 5 次 warning,就必须修代码——这些 warning 在 FPM 下一闪而过,在 FrankenPHP worker 里会固化成内存驻留对象。
真正难的不是发现内存涨,而是区分它是“框架设计使然”还是“bug 导致”。比如 Symfony 的 EventDispatcher 默认缓存所有 listener,worker 模式下这个缓存永不失效;而 Laravel 的 Log::stack() 若没显式 flush,日志处理器也会越攒越多。这些都不是配置能解决的,得动代码。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











