我正在使用Symfony2 Process组件手动管理进程池。
在下面的示例中,我每2秒重新启动2个简单进程并监视发生的情况。重新启动这些进程几百次后应用程序中断。
执行停止,我收到以下PHP警告:
proc_open(): unable to create pipe Too many open files
然后Symfony Process组件抛出以下异常:
[Symfony\Component\Process\Exception\RuntimeException]
Unable to launch a new process.
我手动监控了打开进程的总数,但它从未达到预期的限制。
以下简化的代码段是Symfony2命令的一部分,正在从CLI运行(例如app / console hamster:run):
$processes[] = new Process("ls > /dev/null", null, null, null, 2);
$processes[] = new Process("date > /dev/null", null, null, null, 2);
while (count($processes) > 0) {
foreach ($processes as $i => $process) {
if (!$process->isStarted()) {
$process->start();
continue;
}
try {
$process->checkTimeout();
} catch (\Exception $e) {
// Don't stop main thread execution
}
if (!$process->isRunning()) {
// All processes are timed out after 2 seconds and restarted afterwards
$process->restart();
}
}
usleep($sleep * 1000000);
}
此应用程序正在运行OS X 10.8.4的MAC服务器上运行。
如果能找到解决这个问题根源的任何提示,我将不胜感激。
更新#1:我已将我的功能简化为使用ls
和date
等基本命令,以加快测试速度。在启动和停止大约1000-1500个进程后,它仍然看起来像Process命令失败。
我怀疑proc_close()
没有为每个进程正确调用,但进一步的调查显示情况并非如此。
答案 0 :(得分:2)
文件句柄没有被垃圾收集,所以它们最终会被填满(os限制或php限制,不确定),但你可以通过添加explicit garbage collection call来修复它:
gc_collect_cycles();
usleep($sleep * 1000000);
此外,转发垃圾收集在foreach
循环内不能很好地工作,因为php将临时$foo as $bar => $var
数组变量映射到内存中的方式。如果你需要在代码的那一部分,你可以把它切换成这样的东西,我认为应该允许在for
循环内进行垃圾收集:
$processes[] = new Process("ls > /dev/null", null, null, null, 2);
$processes[] = new Process("date > /dev/null", null, null, null, 2);
$sleep = 0;
do {
$count = count($processes);
for($i = 0; $i < $count; $i++) {
if (!$processes[$i]->isStarted()) {
$processes[$i]->start();
continue;
}
try {
$processes[$i]->checkTimeout();
} catch (\Exception $e) {
// Don't stop main thread execution
}
if (!$processes[$i]->isRunning()) {
// All processes are timed out after 2 seconds and restarted afterwards
$processes[$i]->restart();
}
gc_collect_cycles();
}
usleep($sleep * 1000000);
} while ($count > 0);