1

我正在使用 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()每个进程都没有正确调用它,但进一步调查显示这里不是这种情况。

4

1 回答 1

3

文件句柄没有被垃圾收集,因此它们最终会被填满(操作系统限制或 php 限制,不确定),但您可以通过添加显式垃圾收集调用来修复它:

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);
于 2013-09-12T10:44:23.630 回答