Symfony2 запускает консольную команду в фоновом режиме
Я создал консольную команду для своего проекта symfony2 и хочу выполнить ее с контроллера, не блокируя вывод контроллера (в фоновом режиме).
Обычно выполняется так:
$application = new Application($kernel);
$application->setAutoExit(true);
// AppBundle/Command/UpdateStockCommand.php
$input = new ArrayInput(array(
'command' => 'update:stock',
));
$output = new NullOutput();
$application->run($input, $output);
Но, работая так, пользователь должен будет дождаться завершения задачи, что может занять несколько минут.
Решение:
$kernel = $this->get('kernel');
$process = new \Symfony\Component\Process\Process('nohup php '. $kernel->getRootDir() .'/console update:stock --env='. $kernel->getEnvironment() .' > /dev/null 2>&1 &');
//$process->start();
$process->run();
Ошибки не выдаются, контроллер выводит выходные данные, но задача не выполняется.
Другое решение:
exec('/usr/bin/php '.$this->get('kernel')->getRootDir().'/console update:stock --env=dev > /dev/null 2>&1 &');
нашел здесь Symfony2 - процесс запуска команды symfony2,но не работает на моем примере.
3 ответа
Процессы иерархические
Все процессы в системе имеют собственную иерархию.
Как пример: у нас есть Process A
, после запуска которого мы запускаем Process B
, Если ты убьешь Process A
тогда Process B
убит, потому что Process B
это ребенок Process A
,
Вы проблема
Каждый запрос (http) Apache создает новый дочерний процесс для запуска PHP-кода и возвращает stdoutput клиенту (логика Nginx + PHPFPM - то же самое). И после создания дочернего процесса (через Symfony/Process library
), этот процесс является дочерним процессом apache или fpm. После завершения запроса (ответ на apache или nginx) сервер уничтожает дочерний процесс (где исполняется код PHP).
Решения для вас:
- Хорошая идея для запуска фоновых команд - используйте nohup ( учебник)
- Отличная идея для любых приложений - использовать протокол AMQP между процессами. ( учебник через RabbitMQ)
PS
В моих проектах для выполнения фоновых задач я использую RabbitMQ.
Вы можете установить выход на новый NullOutput.
$output = new NullOutput();
$application->run($input, $output);
Но лучше всего использовать RabbitMqueue
Позвольте мне расширить решение @CerapuStefan
exec('bash -c "exec nohup setsid '.$this->get('kernel')->getRootDir().'/console update:stock --env=dev > /dev/null 2>&1 &"');
nohup
это важно