[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Асинхронное выполнение PHP скрипта
switch
Всем привет!

Задача: на веб морде пользователь заполняет параметры какого-то девайса, из этих параметров формируется файл с командами, которые должны быть залиты на девайс по протоколу телнет. Все вроде бы просто, но этот процесс может занять до 10 минут, так как очередь команд длинной более 1000 строк, а девайс, падла, медленный. Хочется сделать ajax отображение процесса загрузки.

Что сделано: веб морда после нажатия баттона "сабмит" формирует командный файл и запускает скрипт:
system("$path/upload.dlink.php -m $mac -i $ip_addr  -f /tftpboot/dlink$mac &");

внутри этого скрипта формируется отдельный процесс:
$pid = pcntl_fork();
if ($pid == -1) {
}
elseif ($pid) {
echo "Upload started $pid";
exit(0);
} else {
//тут цикл, читающий из файла и выплевывающий посимвольно в девайс
}

по отдельности все работает. А в месте - не хочет.
если пускать аплоадер из командной строки, он прекрасно форкается и выполняет все, что нужно в отдельном процессе, а родитель завершается.
Но если пускаешь его из вебморды, то форка не происходит и страница с system(...) грузится пока не закончится выполнение, причем не смотря на то, что скрипт выполняется как системное приложение!

что можно сделать?



Спустя 45 минут, 32 секунды (4.06.2011 - 11:11) Basili4 написал(а):
Не стоит такие вещи на пыхе делать. Не для этого она.

Спустя 3 минуты, 59 секунд (4.06.2011 - 11:15) Arni написал(а):
Я вижу только один выход.

1. Запустить процес, после, завершить спокойно скрипт.
2. Процес должен при старте поставить флаг (допустем через базу данных) о начале работы и по завершению выставить флаг обратно.
3. Посредством аякс запросов с переодичностью в несколько сикунд, запрашиваем состояние процесса.
4. Как только процесс завершился сообщаем об этом пользователю.

Спустя 8 минут, 25 секунд (4.06.2011 - 11:23) vagrand написал(а):
У меня была похожая проблема. Не мог запустить команду в беграунде из php, т.к. она почему-то ждала ввода. Попробуй модернизировать твою команду как я написал ниже:

system("$path/upload.dlink.php -m $mac -i $ip_addr  -f /tftpboot/dlink$mac < /dev/null > /dev/nul 2>&1 &")

Спустя 6 часов, 3 минуты, 34 секунды (4.06.2011 - 17:27) switch написал(а):
Цитата
Не стоит такие вещи на пыхе делать. Не для этого она.

Гы
предлагаете такую банальную задачку на СРР решать? Пых с загрузкой прекрасно справляется.
Цитата
Я вижу только один выход.

я, конечно, извиняюсь, но КО говорит тоже самое. Но проблема как бэ не в этом, а вот в этом:
Цитата
1. Запустить процес, после, завершить спокойно скрипт.

этот способ:
Цитата
Попробуй модернизировать твою команду...

я пробовал в разных вариантах и до этого. Попробовал сейчас - результат тот же.
Если перенаправлять вообще все:
/tftpboot/dlink$mac < /dev/null > /dev/nul 2>&1 &

то скрипт не отрабатывает.
если не перенаправлять ошибки, не отцеплять от консоли
/tftpboot/dlink$mac < /dev/null  

то работает но не в фоне ну и тп.

Спустя 21 минута, 7 секунд (4.06.2011 - 17:48) killer8080 написал(а):
у вас apache с mod_php?
Тогда может попробовать бэкграунд процесс запускать по другому, то есть upload.dlink.php запускать не как cgi, а через веб, типа такого
if($f = fsockopen("127.0.0.1", 80, &$error, 10)){
$header = "GET /upload.dlink.php HTTP/1.1\n".
"Host:127.0.0.1\n\n";
fwrite($f, $header);
fclose($f);
}

тогда основная страница не будет ждать ответа от upload.dlink.php, в самом же upload.dlink.php прописать
set_time_limit(0); 
ignore_user_abort(true):

а в цикле выода данных добавить (если mod_php конечно)
apache_reset_timeout():

Спустя 10 минут, 37 секунд (4.06.2011 - 17:59) switch написал(а):
Перебрал все возможные варианты заработала такая комбинация:
 /tftpboot/dlink$mac < /dev/null > /dev/null & 

такой способ:
Цитата
Тогда может попробовать бэкграунд процесс запускать по другому

не понял как поможет, ведь pcntl_fork() не работает из под апача, а только как отдельный скрипт.

Всем большое спасибо за помощь, особенно vagrand!

Спустя 17 минут, 40 секунд (4.06.2011 - 18:16) killer8080 написал(а):
switch
а что даёт вилка? Что вы пытаетесь распараллелить? Запросы к девайсу, как я понял, всё равно идут последовательно.

Спустя 6 минут, 30 секунд (4.06.2011 - 18:23) switch написал(а):
Тут сказывается трабла-на-трабле, распаралеливать ничего не нужно.
Просто никак не получалось нормально запускать процесс отцепленным от консоли, только через проставку вроде этой:
#!/bin/sh
nohup nice -n 10 ./upload.dlink.php -m 001B11000011 -i 192.168.0.23 -f /tftpboot/dlink001B11000011 &

А тут скрипт пускается и самостоятельно отцепляется от консоли.
Кстати вот неплохая статья на эту тему:
http://leonid.shevtsov.me/ru/mnogoprocessovye-demony-na-php

Спустя 49 минут, 2 секунды (4.06.2011 - 19:12) killer8080 написал(а):
switch
за ссылку спасибо, интересная статья smile.gif
Но к вашей задаче она по моему имеет мало отношения, вам ведь не нужен демон. Скрипт должен отработать командный файл и завершится. Насколько я понимаю - задача отвязать командный скрипт от интерфейсного. Сам командный файл может работать как угодно хоть в cgi, хоть в mod php. В последнем, единственное, нужно учитывать временные ограничения как php, так и апача, как это обойти я уже приводил пример. Использовать тут форки я думаю будет лишним. А отображение процесса загрузки можно сделать как предложил Arni, т.е. либо в базу либо в файл пишем статус процесса: время запуска, количество команд в очереди, количество выполненных команд и флаг завершения процесса success|fail. Считываем это аяксом и отображаем на странице.
Или я что то не так понимаю? unsure.gif

Спустя 54 минуты, 31 секунда (4.06.2011 - 20:06) switch написал(а):
все правильно понимаете, но это уже все давно сделано. Затык был именно на запуске скрипта как отдельного процесса. У линукса тут есть некоторые своеобразности, которые не только в php скриптах проявляются, поэтому приходится извращаться. Но не думаю, что запуск процесса обращением через сокет это хорошая идея, как-то это неэлегантно.
Форкание процесса никак не сказывается ни на сложности ни на производительности, но оно работает без дополнительных проставок в виде SHELL скрипта и этого достаточно.

Спустя 10 минут, 13 секунд (4.06.2011 - 20:17) killer8080 написал(а):
Может и не элегантно, но работает.
Цитата (switch @ 4.06.2011 - 20:06)
Форкание процесса никак не сказывается ни на сложности ни на производительности, но оно работает без дополнительных проставок в виде SHELL скрипта и этого достаточно.

А вот тут не понял, какое отношение шел скрипты имеют к спосбу запуска php скрипта?

Спустя 10 минут, 21 секунда (4.06.2011 - 20:27) switch написал(а):
Ну яж говорю, что есть проблемы в запуске скрипта в виде фонового процесса. Причем проблемы возникают по-разному, в зависимости откуда их запускать: из командной строки или из пхп. Из-за этого приходится строить каскады из скриптов, чтобы это нормально и как нужно отрабатывало. Не исключаю, что я чего-то не знаю в основных принципах работы линукса и процессов в нем, от того и горожу огороды.

Спустя 8 минут, 34 секунды (4.06.2011 - 20:36) killer8080 написал(а):
Цитата (switch @ 4.06.2011 - 10:26)
Задача: на веб морде пользователь заполняет параметры какого-то девайса, из этих параметров формируется файл с командами, которые должны быть залиты на девайс по протоколу телнет.

То есть скрипт открывает сокет на 23 порт девайса через fsockopen() и последовательно пихает команды из файла или вы это делаете как то по другому?
Можете показать кусок скрипта который это делает?

Спустя 9 минут, 58 секунд (4.06.2011 - 20:46) switch написал(а):
да там все банально:
        $commands=file_get_contents($options['f']);
$commands=explode("\n",$commands);
$ip=$options['i'];
if (strlen($ip)) {
if ($fp=fsockopen($ip,23))
{
fputs($fp,$conn1);
usleep(100000);
fputs($fp,$conn2);
usleep(100000);
GetResponse($r);
$r=explode("\n",$r);
$loginprompt=$r[count($r)-1];
fputs($fp,$options['u']."$\r");

usleep(100000);
fputs($fp,$options['p']."\r");
sleep(3);
GetResponse($r);
$r=explode("\n",$r);
$total=count($commands);
foreach($commands as $key => $command)
{

for($i=0; $i<strlen($command); $i++)
{
usleep(8000);
fputs($fp, $command{$i});
}
fputs($fp,"\r");
usleep(150000);
GetResponse($r);
$r=explode("\n",$r);


};
sleep(3);

function GetResponse(&$r) {
global $fp;
$r='';
do {
$out=fread($fp,1000);
$r.=$out;
$s=socket_get_status($fp);
} while ($s['unread_bytes']); }

я не стал анализировать ответы, а просто подобрал задержки так, чтобы все путем проходило.

Спустя 29 минут, 1 секунда (4.06.2011 - 21:15) killer8080 написал(а):
Цитата (switch @ 4.06.2011 - 20:46)
я не стал анализировать ответы, а просто подобрал задержки так, чтобы все путем проходило

а вот это не правильно, нельзя полагаться на таймауты, если они слишком большие скрипт будет тормозить понапрасну, если маленькие будет сбоить. Нужно как то так
function GetResponse(&$r){
global $fp;
while(!feof($fp)){
$r .= fgets($fp, 1000);
}
}
анализировать результат не обязательно, но вызывать функцию после каждого fputs, тогда скрипт будет автоматом согласован по времени. Может это одна из проблем?

Спустя 23 минуты, 10 секунд (4.06.2011 - 21:38) switch написал(а):
;)))
конструкция
!feof($fp)

работать не будет, так как работа ведется с сокетом, который отдает данные потоком и никакого EOF вы в потоке не увидите. Поэтому нужно смотреть в socket_get_status() на наличие оставшихся в буфере данных.
Получение данных не проблема: появились в буфере - обработали. Проблема в том, что девайс тупо не может обработать символьный поток с большой скоростью. Если пихать в него сразу 5 кб данных то часть он обработает, а потом зависнет. Поэтому вывожу команды в девайс посимвольно с задержкой между символами.
Анализировать ответ готовности тоже не айс: если дать ему сразу три команды, он обработает каждую и на каждую ответит ОК, а возиться с определением какая строка прошла или нет - просто лень.

Проблему большого кол-ва загружаемых данных решил другим путем: не все блоки конфига требуется загружать каждый раз, поэтому пользователь может отметить те блоки, которые следует изменять.

Конечно, можно было сделать проверку на изменения, но лень. Эта задача есть частный случай более общей системы по конфигурированию разнообразных сетевых девайсов (в основном VoIP), все сделано достаточно гибко, но некоторые девайсы выбиваются из общего ряда, для них приходится придумывать обработочки.

Спустя 5 минут, 11 секунд (4.06.2011 - 21:43) killer8080 написал(а):
feof работает не только с fopen, но и с fsockopen
Цитата
Указатель на файл должен быть корректным и указывать на файл, успешно открытый функциями fopen() или fsockopen() (и все еще не закрытым функцией fclose()).

в любом случае такой вариант будет работать надежно и максимально быстро, что в вашем случае весьма актуально!

Спустя 26 минут, 8 секунд (4.06.2011 - 22:09) killer8080 написал(а):
Цитата (switch @ 4.06.2011 - 21:38)
Если пихать в него сразу 5 кб данных то часть он обработает, а потом зависнет. Поэтому вывожу команды в девайс посимвольно с задержкой между символами.
Анализировать ответ готовности тоже не айс: если дать ему сразу три команды, он обработает каждую и на каждую ответит ОК, а возиться с определением какая строка прошла или нет - просто лень.

В смысле виснет? То есть до ребута?
А анализировать ответ не обязательно, просто запросы будут за синхронизированы с ответами. Если девайс начнёт тормозить, просто увеличится время между запросами, по готовности последнего. Если прошивка не глючная, по идее проблем быть не должно.

Спустя 8 часов, 47 минут, 2 секунды (5.06.2011 - 06:56) switch написал(а):
Я не очень понимаю в чем вы меня пытаетесь переубедить.
Одно дело рассуждать теоретически, "по идее", а другое дело - многолетний опыт работы с девайсами и хотя бы то, что несколько разных вариантов железки лежат на столе и работают.

Спустя 1 час, 28 минут, 47 секунд (5.06.2011 - 08:25) kirik написал(а):
switch
Была похожая проблема (Тыц). Хотя на некоторых машинах и "/usr/local/bin/php /path/to/my/script.php >> /dev/null &" прекрасно работало. Не разбирался от чего зависит...

Цитата (switch @ 4.06.2011 - 10:27)
предлагаете такую банальную задачку на СРР решать?

Можно ведь shell скрипт написать, чтобы php не дёргать лишний раз. Хотя разницы впринципе нет.

Спустя 59 минут, 58 секунд (5.06.2011 - 09:25) switch написал(а):
зачем плодить сущности без надобности?
Я хоть и могу накодить на шелле хоть вебсайт, но зачем оно нужно? Есть более подходяще средства, а ПХП как язык мне нравится больше тех остальных 10..15 языков, которые я знаю.

Я очень ленивый и для меня работа с системными функциями проще, быстрее и красивее получается на shell, работа с данными - на пхп. Так уж получилось, что пхп стал серьезным прикладным языком, лично я этому рад.

А работать с сокетами на баше это чистой воды изврат.

Спустя 2 минуты, 48 секунд (5.06.2011 - 09:28) kirik написал(а):
switch
Я не настаиваю smile.gif
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.