致命错误使用Symfony控制台时打开的文件太多


Fatal Error too many open files when using Symfony Console

我正在运行一个控制台进程,该进程侦听事件并向控制台写入.以指示进度。这可能会运行10-15分钟,但大约10分钟后,我会出现以下一系列致命的控制台错误:

[2014-10-14 11:13:49] local.ERROR: 500 - fopen(php://stderr): failed to open stream: operation failed @ /
exception 'ErrorException' with message 'fopen(php://stderr): failed to open stream: operation failed' in /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php:53
Stack trace:
#0 [internal function]: Illuminate'Exception'Handler->handleError(2, 'fopen(php://std...', '/vagrant/www/te...', 53, Array)
#1 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php(53): fopen('php://stderr', 'w')
#2 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/src/eComEvo/TaskRunner/Traits/Events.php(262): Symfony'Component'Console'Output'ConsoleOutput->__construct()
#3 [internal function]: eComEvo'TaskRunner'TaskRunner::eComEvo'TaskRunner'Traits'{closure}('.', 280, Array)
#4 /vagrant/www/test.dev/laravel/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(199): call_user_func_array(Object(Closure), Array)
#5 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/vendor/illuminate/support/Illuminate/Support/Facades/Facade.php(211): Illuminate'Events'Dispatcher->fire('task.group.prog...', Array)
#6 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/src/eComEvo/TaskRunner/Traits/Events.php(92): Illuminate'Support'Facades'Facade::__callStatic('fire', Array)
#7 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/src/eComEvo/TaskRunner/Traits/Events.php(92): Illuminate'Support'Facades'Event::fire('task.group.prog...', Array)
#8 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/src/eComEvo/TaskRunner/Traits/Events.php(205): eComEvo'TaskRunner'TaskRunner::event('progress', '.', 280, '423403-23463321-604...', 'event')
#9 /vagrant/www/test.dev/laravel/app/models/TaskRunner.php(280): eComEvo'TaskRunner'TaskRunner::progress('.', 280, '2423-521354...')
#10 /vagrant/www/test.dev/laravel/app/library/ecomevo/task/src/eComEvo/TaskRunner/Commands/TaskRunnerImportOrdersCommand.php(128): TaskRunner::import('15', NULL, Array, '16', '4')
#11 /vagrant/www/test.dev/laravel/vendor/laravel/framework/src/Illuminate/Console/Command.php(112): eComEvo'TaskRunner'Commands'TaskRunnerImportOrdersCommand->fire()
#12 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Command/Command.php(252): Illuminate'Console'Command->execute(Object(Symfony'Component'Console'Input'ArgvInput), Object(Symfony'Component'Console'Output'ConsoleOutput))
#13 /vagrant/www/test.dev/laravel/vendor/laravel/framework/src/Illuminate/Console/Command.php(100): Symfony'Component'Console'Command'Command->run(Object(Symfony'Component'Console'Input'ArgvInput), Object(Symfony'Component'Console'Output'ConsoleOutput))
#14 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(889): Illuminate'Console'Command->run(Object(Symfony'Component'Console'Input'ArgvInput), Object(Symfony'Component'Console'Output'ConsoleOutput))
#15 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(193): Symfony'Component'Console'Application->doRunCommand(Object(eComEvo'TaskRunner'Commands'TaskRunnerImportOrdersCommand), Object(Symfony'Component'Console'Input'ArgvInput), Object(Symfony'Component'Console'Output'ConsoleOutput))
#16 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(124): Symfony'Component'Console'Application->doRun(Object(Symfony'Component'Console'Input'ArgvInput), Object(Symfony'Component'Console'Output'ConsoleOutput))
#17 /vagrant/www/test.dev/laravel/artisan(59): Symfony'Component'Console'Application->run()
#18 {main} [] []
[2014-10-14 11:13:50] local.ERROR: 500 - proc_open(): unable to create pipe Too many open files @ /
exception 'ErrorException' with message 'proc_open(): unable to create pipe Too many open files' in /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php:983
Stack trace:
#0 [internal function]: Illuminate'Exception'Handler->handleError(2, 'proc_open(): un...', '/vagrant/www/te...', 983, Array)
#1 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(983): proc_open('stty -a | grep ...', Array, NULL, NULL, NULL, Array)
#2 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(799): Symfony'Component'Console'Application->getSttyColumns()
#3 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(760): Symfony'Component'Console'Application->getTerminalDimensions()
#4 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(690): Symfony'Component'Console'Application->getTerminalWidth()
#5 /vagrant/www/test.dev/laravel/vendor/laravel/framework/src/Illuminate/Console/Application.php(201): Symfony'Component'Console'Application->renderException(Object(ErrorException), Object(Symfony'Component'Console'Output'StreamOutput))
#6 /vagrant/www/test.dev/laravel/vendor/symfony/console/Symfony/Component/Console/Application.php(131): Illuminate'Console'Application->renderException(Object(ErrorException), Object(Symfony'Component'Console'Output'StreamOutput))
#7 /vagrant/www/test.dev/laravel/artisan(59): Symfony'Component'Console'Application->run()
#8 {main} [] []

以下是10分钟后触发这些错误的代码:

static::listen(
    'progress',
    function ($indicator = '.', $line = null, $id = 0) {
        $output = new 'Symfony'Component'Console'Output'ConsoleOutput();
        $extra = [];
        if (static::$debug > 2) {
            if (!empty($line) && $line !== true)
                $extra[] = "line=$line";
            if (!empty($id)) {
                if (is_array($id))
                    $id = implode(',', $id);
                if (!empty($id))
                    $extra[] = "ID=$id";
            }
        }
        if (!empty($extra))
            $output->writeln('[' . implode(', ', $extra) . ']');
        elseif ($line === true)
            $output->writeln("'n$indicator");
        else
            $output->write($indicator);
        static $gc_count = 0;
        $gc_count++;
        if ($gc_count > 25) {
            $gc_count = 0;
            gc_collect_cycles();
        }
    },
    'event'
);

我已经四处寻找解决方案,所以我添加了gc_collect_cycles()行,希望这会有所帮助,但它并不能解决问题。

查看Symfony ConsoleOutput代码,它总是在执行fopen($outputStream, 'w'),但我找不到它再次关闭它的任何位置
所以你可以在你的功能结束时自己关闭它:

$resource = $output->getStream();
fclose($resource);

或者只尝试实例化一次,然后重用它。

注意:实际上,我认为如果不再引用匿名函数,垃圾回收应该关闭它。很难,我不知道static::listen方法在做什么,所以可能值得尝试手动关闭它。