workerman子进程内存超限应通过onworkerstart中定时调用memory_get_usage(true)检测并exit(0)触发优雅退出;主进程捕获后自动fork新子进程补位,实现单进程精准淘汰而非全量重启。

Workerman子进程内存超限怎么触发优雅退出?
Workerman本身不提供内存阈值自动重启机制,必须手动在onWorkerStart中注册定时器,用memory_get_usage(true)轮询检查,超限时调用exit——主进程会捕获退出并自动fork新子进程补位。
关键点在于:不是“重启整个服务”,而是让单个Worker子进程主动退出,依赖Master的默认保活逻辑完成替换。这比杀进程更可控,也不会中断其他正在运行的Worker。
-
memory_get_usage(true)返回的是当前进程实际分配的内存(含PHP内部结构),比memory_get_usage()更准确; - 检查频率建议设为5–30秒,太密影响性能,太疏可能OOM前没来得及响应;
- 务必在
onWorkerStart里注册,不能放在onMessage或onConnect中——那些回调是事件驱动的,不保证定时执行; - 退出前可记录日志,例如
echo "Worker {$worker->id} exiting due to memory usage: " . memory_get_usage(true) . "\n";,方便后续排查泄漏点。
为什么不能用pcntl_signal + SIGUSR1或reload?
因为php start.php reload或向主进程发SIGUSR1,是让所有Worker统一平滑重启,它不区分哪个进程内存高。你真正想做的,是「精准淘汰」那个吃内存的Worker,而不是全量刷新——否则健康进程也被拉下水,反而放大抖动。
另外,Worker::reload()是主进程发起的,子进程无法自行调用;而pcntl_signal在子进程中注册信号处理器后,仍需等待事件循环进入pcntl_signal_dispatch()才生效,不适合做实时内存响应。
- 子进程内不能调用
Worker::reload(),该方法只在主进程上下文有效; -
kill -USR1 $pid只能作用于主进程,对Worker子进程发信号无意义(它们不监听SIGUSR1); - 靠外部工具(如supervisor)监控内存,只能杀主进程,会中断全部服务,违背“优雅”和“局部”原则。
完整代码片段怎么写?
以下是在onWorkerStart中嵌入内存检查的最小可行示例,注意路径、单位和退出逻辑:
$worker->onWorkerStart = function ($worker) {
// 每10秒检查一次,阈值设为128MB(字节)
$maxMemory = 128 * 1024 * 1024;
\Workerman\Timer::add(10, function () use ($worker, $maxMemory) {
$usage = memory_get_usage(true);
if ($usage > $maxMemory) {
echo "Worker {$worker->id} memory {$usage} > {$maxMemory}, exiting...\n";
exit(0); // 主进程会捕获此退出并fork新进程
}
});
};
- 不要用
die()或exit(1),exit(0)表示正常退出,Master更倾向认为是“主动让位”,而非故障; - 如果项目用了
opcache或大量静态变量,memory_get_usage(true)可能偏高,建议先在测试环境跑一段时间,观察基线再设阈值; - 该逻辑对每个Worker独立生效,无需加锁或跨进程同步。
容易被忽略的边界情况
内存检查看似简单,但真实部署时几个细节常导致失效:
- Worker进程若卡死在阻塞IO(如
fgets(STDIN)、未设超时的curl_exec),定时器根本不会触发——得配合pcntl_async_signals(false)和信号兜底,或改用异步IO; - 某些扩展(如
redis连接池)会缓存大量资源,memory_get_usage未必反映真实压力,必要时要结合Redis::info('memory')等业务指标; - 如果Worker做了长时间计算(比如图像处理),10秒检查间隔可能来不及响应,需缩短周期或在计算关键路径中插桩检查。
最终效果取决于你是否把内存视为“可预测的资源消耗”,而不是“偶尔飙高的噪音”。真要稳,就得在代码层埋点、测基线、设阈值、留余量——Workerman只提供钩子,不替你做判断。











