Асинхронная оболочка exec в PHP
У меня есть PHP script, который должен вызывать оболочку script, но все равно не заботится о выходе. Оболочка script выполняет ряд SOAP-вызовов и медленно завершается, поэтому я не хочу замедлять PHP-запрос, пока он ждет ответа. Фактически, PHP-запрос должен иметь возможность выйти без завершения процесса оболочки.
Я просмотрел различные функции exec()
, shell_exec()
, pcntl_fork()
и т.д., но никто из них, похоже, не предлагает именно то, что я хочу. (Или, если они это делают, мне не ясно, как.) Любые предложения?
Ответы
Ответ 1
Если он "не заботится о выходе", не мог ли вызов exec в script с &
выполнить фоновый процесс?
РЕДАКТИРОВАТЬ - добавив, что @AdamTheHut прокомментировал это сообщение, вы можете добавить это к вызову exec
:
" > /dev/null 2>/dev/null &"
Это перенаправит как stdio
(сначала >
), так и stderr
(2>
) в /dev/null
и запустится в фоновом режиме.
Есть и другие способы сделать то же самое, но это простейшее для чтения.
Альтернатива вышеупомянутому двойному перенаправлению:
" &> /dev/null &"
Ответ 2
Я использовал в для этого, так как он действительно запускает независимый процесс.
<?php
`echo "the command"|at now`;
?>
Ответ 3
В linux вы можете сделать следующее:
$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
Это выполнит команду в команде prompty, а затем просто вернет PID, который вы можете проверить нa > 0, чтобы убедиться, что он сработал.
Этот вопрос аналогичен: Есть ли у PHP потоковая передача?
Ответ 4
php-execute-a-background-process есть некоторые полезные советы. Я думаю, что мой довольно хорошо, но я предвзятый:)
Ответ 5
Для всех пользователей Windows: я нашел хороший способ запуска асинхронного PHP script (на самом деле он работает практически со всеми).
Он основан на командах popen() и pclose(). И хорошо работает как в Windows, так и в Unix.
function execInBackground($cmd) {
if (substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
Оригинальный код: http://php.net/manual/en/function.exec.php#86329
Ответ 6
В Linux вы можете запустить процесс в новом независимом потоке, добавив амперсанд в конце команды
mycommand -someparam somevalue &
В Windows вы можете использовать команду "start" DOS
start mycommand -someparam somevalue
Ответ 7
правильный путь (!) для этого -
- вилка()
- setsid()
- execve()
fork forks, setsid сообщают текущему процессу стать ведущим (без родителя), execve сообщит, что вызывающий процесс будет заменен вызываемым. так что родитель может выйти, не затрагивая ребенка.
$pid=pcntl_fork();
if($pid==0)
{
posix_setsid();
pcntl_exec($cmd,$args,$_ENV);
// child becomes the standalone detached process
}
// parent stuff
exit();
Ответ 8
Я использовал это...
/**
* Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.
* Relies on the PHP_PATH config constant.
*
* @param string $filename file to execute
* @param string $options (optional) arguments to pass to file via the command line
*/
function asyncInclude($filename, $options = '') {
exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}
(где PHP_PATH
является константой, определенной как define('PHP_PATH', '/opt/bin/php5')
или аналогичной)
Он передает аргументы через командную строку. Чтобы прочитать их на PHP, см. argv.
Ответ 9
Единственный способ, которым я нашел, что действительно работал у меня, был:
shell_exec('./myscript.php | at now & disown')
Ответ 10
Вы также можете запустить PHP script как daemon или cronjob: #!/usr/bin/php -q
Ответ 11
Используйте имя fifo.
#!/bin/sh
mkfifo trigger
while true; do
read < trigger
long_running_task
done
Затем, когда вы хотите запустить долго выполняемую задачу, просто напишите новую строку (неблокируясь в файл триггера.
Пока ваш ввод меньше, чем PIPE_BUF
, и это одна операция write()
, вы можете записать аргументы в fifo и показать их как $REPLY
в script.
Ответ 12
без использования очереди, вы можете использовать proc_open()
следующим образом:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w") //here curaengine log all the info into stderror
);
$command = 'ping stackoverflow.com';
$process = proc_open($command, $descriptorspec, $pipes);
Ответ 13
Я также нашел компонент Symfony Process, полезный для этого.
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
// ... run process in background
$process->start();
// ... do other things
// ... if you need to wait
$process->wait();
// ... do things after the process has finished
Посмотрите, как это работает в GitHub repo.