【问题标题】:Symfony2 Process component - unable to create pipe and launch a new processSymfony2 进程组件 - 无法创建管道并启动新进程
【发布时间】:2013-09-11 15:12:56
【问题描述】:

我正在使用 Symfony2 Process 组件来手动管理进程池。

在下面的示例中,我每 2 秒重新启动 2 个简单进程并监视发生的情况。重启这些进程几百次后,应用程序就会中断。

执行已停止,我收到以下 PHP 警告:

proc_open(): unable to create pipe Too many open files

然后 Symfony 进程组件抛出以下异常:

[Symfony\Component\Process\Exception\RuntimeException]  
Unable to launch a new process.   

我已经手动监控了打开进程的总数,它从未达到预期的限制。

以下简化的 sn-p 是 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:我已经简化了我的函数以使用lsdate 等基本命令,以加快测试速度。在启动和停止大约 1000-1500 个进程后,Process 命令看起来仍然失败。

我怀疑没有为每个进程正确调用proc_close(),但进一步调查发现这里不是这种情况。

【问题讨论】:

    标签: php symfony process symfony-process


    【解决方案1】:

    文件句柄没有被垃圾回收,因此它们最终会被填满(操作系统限制或 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);
    

    【讨论】:

    • 好点。我没有考虑手动调用垃圾收集器的必要性。使用gc_collect_cycles() 执行一些额外的基准测试(重新启动进程 100,000 多次)确认了您的解决方案。谢谢!
    • 一个更好的方法是在完成后-&gt;stop(0)流程实例,这将有效地关闭打开的文件描述符,而不必依赖垃圾收集器调用__destruct
    • 你用这个救了我的命!需要睡觉吗?
    猜你喜欢
    • 1970-01-01
    • 2019-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    相关资源
    最近更新 更多