Компонент Symfony2 Process - невозможно создать канал и запустить новый процесс
Я использую компонент 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);
}
Это приложение работает на сервере MAC под управлением OS X 10.8.4.
Буду признателен за любые советы о том, как продолжить корень этой проблемы.
Обновление № 1: я упростил свою функцию для работы с основными командами, такими как ls
а также date
для более быстрого тестирования. Выглядит так, будто команда Process завершается неудачно после запуска и остановки около 1000-1500 процессов.
Я подозревал что proc_close()
неправильно вызывался для каждого процесса, но дальнейшее расследование показало, что это не так.
1 ответ
Дескрипторы файла не собираются мусором, поэтому они в конечном итоге заполняются (os os или php limit, не уверены), но вы можете это исправить, добавив явный вызов сборки мусора:
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);