Асинхронная оболочка exec в PHP
-
03-07-2019 - |
Вопрос
У меня есть PHP-скрипт, который должен вызывать скрипт оболочки, но не заботится о выводе. Сценарий оболочки выполняет несколько вызовов SOAP и выполняется медленно, поэтому я не хочу замедлять запрос PHP, пока он ожидает ответа. Фактически, PHP-запрос должен быть в состоянии завершиться без завершения процесса оболочки.
Я просмотрел различные функции exec ()
, shell_exec ()
, pcntl_fork ()
и т. д., но ни одну из кажется, они предлагают именно то, что я хочу. (Или, если они это сделают, мне не ясно, как.) Есть предложения?
Решение
Если он "не заботится о выводе", нельзя ли вызвать exec для скрипта с помощью & amp;
для создания фона процесса?
РЕДАКТИРОВАТЬ - учитывая, что @ AdamTheHut прокомментировал этот пост, вы можете добавить его в вызов exec
:
" > /dev/null 2>/dev/null &"
Это перенаправит stdio
(сначала >
) и stderr
( 2 >
) в / dev / null
и работать в фоновом режиме.
Есть и другие способы сделать то же самое, но это проще всего прочитать.
<Ч>Альтернатива вышеупомянутому двойному перенаправлению:
" &> /dev/null &"
Другие советы
Я использовал для этого at , поскольку он действительно запускает независимый процесс.
<?php
`echo "the command"|at now`;
?>
Для всех пользователей 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
В Linux вы можете делать следующее:
$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
Это выполнит команду в командной строке, а затем просто вернет PID, который вы можете проверить на наличие > 0, чтобы убедиться, что это сработало.
Этот вопрос похож: Есть ли в PHP многопоточность?
php-execute-a-background-process предлагает несколько полезных советов , Я думаю, что мой довольно хорошо, но я предвзятый :)
В Linux вы можете запустить процесс в новом независимом потоке, добавив амперсанд в конце команды
mycommand -someparam somevalue &
В Windows вы можете использовать " start " Команда DOS
start mycommand -someparam somevalue
правильный способ (!) сделать это -
<Ол> <Литий> вилка () литий> <Литий> setsid () литий> <Литий> execve () литий> Ол>fork-вилки, setsid указывает текущему процессу стать ведущим (без родительского элемента), execve сообщает вызывающему процессу о необходимости замены вызываемым. так что родитель может выйти, не влияя на ребенка.
$pid=pcntl_fork();
if($pid==0)
{
posix_setsid();
pcntl_exec($cmd,$args, правильный способ (!) сделать это -
<Ол>
<Литий> вилка () литий>
<Литий> setsid () литий>
<Литий> execve () литий>
Ол>
fork-вилки, setsid указывает текущему процессу стать ведущим (без родительского элемента), execve сообщает вызывающему процессу о необходимости замены вызываемым. так что родитель может выйти, не влияя на ребенка.
<*>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
- это константа, определенная как 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
Посмотрите, как это работает, в /it> repo . р>
Используйте именованный fifo.
#!/bin/sh
mkfifo trigger
while true; do
read < trigger
long_running_task
done
Затем, когда вы захотите запустить долгосрочное задание, просто напишите новую строку (неблокирующую в файле триггера.
Пока ваш ввод меньше PIPE_BUF
и это одна операция write ()
, вы можете записывать аргументы в fifo и отображать их как $ REPLY
в скрипте.
Вы также можете запустить скрипт PHP как демон или cronjob : #! / usr / bin / php -q
без очереди использования вы можете использовать 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);