Асинхронный Shell exec в PHP
у меня есть php-скрипт, который должен вызывать сценарий оболочки, но не заботится о выходе. Сценарий оболочки делает несколько вызовов SOAP и медленно завершается, поэтому я не хочу замедлять запрос PHP, пока он ждет ответа. Фактически, запрос PHP должен иметь возможность выйти без завершения процесса оболочки.
Я просмотрел различные exec()
, shell_exec()
, pcntl_fork()
, etc. функции, но ни одна из них не предлагает именно то, что я хочу. (Или, если они это делают, мне непонятно как. Есть предложения?
13 ответов
если он "не заботится о выходе", не может ли exec скрипту вызываться с помощью &
для фона процесса?
редактировать - применив @AdamTheHut прокомментировал этот пост, вы можете добавить к вызову exec
:
" > /dev/null 2>/dev/null &"
это перенаправит оба stdio
("первый"!--5-->) и stderr
(2>
) к /dev/null
и работать в фоновом режиме.
есть и другие способы сделать то же самое, но это самое простое для чтения.
альтернатива вышеуказанному двойному перенаправлению:
" &> /dev/null &"
Я at для этого, так как это действительно начало независимого процесса.
<?php
`echo "the command"|at now`;
?>
в linux вы можете сделать следующее:
$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
Это выполнит команду в командной строке, а затем просто вернет PID, который вы можете проверить > 0, чтобы убедиться, что он работал.
этот вопрос сродни: имеет ли PHP поток?
для всех пользователей Windows: я нашел хороший способ запустить асинхронный PHP-скрипт (на самом деле он работает почти со всем).
Он основан на командах 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
php-execute-a-background-process есть несколько хороших предложений. Я думаю, что мой довольно хорош, но я предвзят:)
в Linux вы можете запустить процесс в новом независимом потоке, добавив амперсанд в конце команды
mycommand -someparam somevalue &
в Windows вы можете использовать команду" Пуск " DOS
start mycommand -someparam somevalue
правильный путь(!) сделать это значит
- вызов Fork()
- 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's stuff
exit();
я использовал это...
/**
* 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
является const определяется как define('PHP_PATH', '/opt/bin/php5')
или аналогичные)
он передает Аргументы через командную строку. Чтобы прочитать их в PHP, см. argv.
единственный способ, который я нашел, что действительно работал для меня:
shell_exec('./myscript.php | at now & disown')
Я нашел Компонент Процесса Symfony полезное для этого.
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.
используйте имя fifo.
#!/bin/sh
mkfifo trigger
while true; do
read < trigger
long_running_task
done
тогда, когда вы хотите, чтобы начать длительные задачи, просто напишите строку (незаблокированном на курок файл.
пока ваш вход меньше, чем PIPE_BUF
и один write()
операция, вы можете записать аргументы в fifo и показать их как $REPLY
в скрипте.
без очереди использования вы можете использовать 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);