параллельная обработка данных

rafael

Новичок
Нужно запустить процедуру, и продолжить работу не дожидаясь её окончания.

Алгоритм моего скрипта:

1. пользователь заполняет форму добавления нового элемента в БД.
2. скрипт обрабатывает POST-входящие, и добавляет (INSERT INTO) запись в таблицу БД.
3. скрипт выполняет SQL-запрос, который на основании добавленной записи, делает UPDATE в других записях. время выполнения несколько секунд.
4. скрипт завершает работу.

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

Задача: отправить пункт 3 выполняться параллельно и, не дожидаясь результатов его выполнения, завершить работу скрипта.

Я вижу только такие решения:

1. Вынести процедуру (функцию) в отдельный скрипт, доступный по http. Использовать file_get_contents и в заголовках (с помощью stream_context_create()) прописать минимальный таймаут ожидания ответа от сервера.

2. Вынести процедуру (функцию) в отдельный скрипт и запускать его посредством exec() с добавлением амперсанда в конце коммандной строки. Например, "fget http://blablabla/1/ &" или "php c:/web/blablabla/1.php &"

Но! Всё это как то не красиво, хаки какие то. Может в php есть возможность параллельной обработки функций? Может я что-то упустил? Если, нет, то я хоть буду знать, что по другому никак.

Спасибо.
 

ypeskov

Новичок
зависит от того насколько быстро инсерт должен появиться в базе.

- пишете SQL запрос в файл. cron через определенный промежуток времени выбирает запросы и выполняет.
- форкаете процесс и в форкнутом выполняете исключительно insert, а основной процесс продолжается и завершается
 

rafael

Новичок
зависит от того насколько быстро инсерт должен появиться в базе.

- пишете SQL запрос в файл. cron через определенный промежуток времени выбирает запросы и выполняет.
- форкаете процесс и в форкнутом выполняете исключительно insert, а основной процесс продолжается и завершается
Понял мысль, но INSERT должен проявить себя сразу же. Это обязательное условие. Другое дело, если в файл писать тот SQL запрос, который выполняется несколько секунд. Но я так не люблю скрипты, живущие своей жизнью от остальных. Про них могут забыть при переносе сайта на другой хостинг и смене программиста (меня). Хочется ещё более красивого решения. Ещё будут варианты?
 

rafael

Новичок
Если используешь php-fpm, то можно использовать http://php-fpm.org/wiki/Features#fastcgi_finish_request.28.29.
Нет не использую, да и плохо понимаю что это.

Правда пользователь об ошибке не узнает.
Ничего страшного. Вероятность успешного выполнения 99%. А при неудачном 1% последствия будут незначительными.
 

rafael

Новичок

tz-lom

Продвинутый новичок
если это так критично быть может стоит пересмотреть сам тормозной запрос?
а может быть достаточно предварительно выводить страничку "сейчас придётся подождать"?
просто есть сомнения в необходимости ожидания ответа в отдельном потоке,в РНР обычно тормозит SQL а всё остальное относительно быстро,так что стоит ли экономить эту десятую секунды,создавая форк (который по выходу ещё и все дескрипторы закроет,будьте осторожны)
P.S.
я вижу вы под вендой кодите, удачи, в вантузе форки работать не будут
 

ypeskov

Новичок
а, я не обратил на винду внимания. там да. этот номер не пройдет.
 

rafael

Новичок
Ребята, вот мой отчёт:

1. попробовал pcntl_fork() .

плюсы: действительно клёвая штука, если речь идёт о "Hello world".

минусы:
- пришлось дёрнуть админа (т.к. "--enable-pcntl")
- в статье написано
После pcntl_fork у скрипта начинается шизофрения
- это ощутил.
-
который по выходу ещё и все дескрипторы закроет,будьте осторожны
- это тоже ощутил.
-
Кстати, хочу предупредить: если в программе есть ресурсы соединения с БД, то перед ветвлением их лучше закрыть, и в дочерних процессах открывать свои, которые должны завершаться в конце жизни процессов.
- это меня вывело из состояния равновесия, и на этом я закончил тратить время на pcntl_fork

2. Потом я решил использовать

exec('wget "http://mysite.ru/1.php?var1=1&var2=2" &');

минусы:
- похоже амперсанд в конце не помог, то ли wget не способен в фоне запустить, то ли wget в процессе работы выводил миллиард сообщений на экран. Не могу утверждать, но вроде бы exec ждал загрузки странички.
- разные сессии.

3. В итоге остановился на curl_exec() с таймаутом.

public function quick_http($url) {
$ch = curl_init();
curl_setopt_array($ch, array (CURLOPT_URL => $url, CURLOPT_HEADER => 0, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_TIMEOUT => 1));
curl_exec($ch);
curl_close($ch);
}

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

Всем спасибо.
 

Major

Новичок
Есть среди паттернов проектирования архитектуры приложений, такая штука, как сервер (отложеных) задач/очередей. Из готовых, можно взять Gearman. (http://php.net/manual/en/book.gearman.php)

Подробнее есть статья на хабре, есть в журнале "Системный администратор" (майский номер этого года), на самом сайте http://gearman.org/index.php?id=php_-_synchronous_image_resize

В данном случае это наиболее парвильное решение "распаралеливания".
 

Major

Новичок
Вкрадце суть. Клиент (в данном случае скрипт), ставит задачу серверу. Сервер, по мере поступления задач и их приоритета будет их выпролнять в фоновом режиме, паралельно работе веб-сервера.
 

MiRacLe

просто Чудо
Gearman, rabbitMQ: это всё хорошо, модно и у всех на слуху, но не для гостевой книги любителя хомячков. Для "сельской местности" подходит :

PHP:
              if (false !== ($fh = @fsockopen($_SERVER['SERVER_ADDR'], $_SERVER['SERVER_PORT'],  
    $errno, $errstr, 0.01))) {  
                  fputs($fh,  
                      "GET $job_url HTTP/1.0\r\n"  
                          . "Host: {$_SERVER['HTTP_HOST']}\r\n\r\n"  
                  );  
                  fgets($fh,32);  
                  fclose($fh);  
              }
Не требует никаких расширений PHP и дополнительного софта, работает *на всех хостингах, прост в отладке, прозрачен в работе.
 

Major

Новичок
MiRacLe
Для гостевой книги сойдет INSERT DELAYED ... Если это гостевая для сельской местности, пусть поставит индексы в БД и вообще оптимизирует ее, чтобы небыло запросов на несколько секунд... У него там 10 000 записей чтоли?
 

MiRacLe

просто Чудо
Major Есть масса задач, решение которых сиюминутно не требуется (не влияет на состоянии системы в текущий момент) и которые выполняются дольше комфортного таймаута. И это не всегда связано с базой данных, т.е. "insert delayed" и "пусть поставит индексы" не поможет быстрее отправить почту или поработать с графикой, обработать отчёт или запросить данные из внешнего "медленного" источника, в тоже время городить фоновое выполнение таких простых задач на gearmand или rabbitmq не всегда разумно - этого просто не позволят имеющиеся ресурсы.
 

fixxxer

К.О.
Партнер клуба
Ой, ну какие еще прости хоспади, rabbitmq. Очередь на мыскле и ее разбиралка крон скриптом это от силы 100 строк кода. Да хоть, блин, на plain text файлах. :) Главное не забыть правильно лочить элементы.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Малчык, ты перед тем как брать в руки ружжо, инструкцию прочти - буквально и полностью, чтоб самострелом не стать.


>После pcntl_fork у скрипта начинается шизофрения, все дескрипторы закроет
>это меня вывело из состояния равновесия, и на этом я закончил тратить время на pcntl_fork
ниче не начинается, форкать надо в начале работы скрипта, а не после соединения с базой и открытия файлов (сессий)


>2. Потом я решил использовать
>exec('wget "http://mysite.ru/1.php?var1=1&var2=2" &');
гы, а до этого ты что, форкался из процесса апача? :)


- похоже амперсанд в конце не помог
чтобы процесс стал демоном, его надо отключить от консоли >/dev/null 2>&1
за 5 минут ты в этом не разберешься


>когда время поджимает, а со всех сторон сыпятся жалобы от пользователей
писать заявление по профнепригодности и менять профессию :)
это вообще-то нормальное состояние работы программиста
 

rafael

Новичок
>Малчык
>ниче не начинается, форкать надо в начале работы скрипта, а не после соединения с базой и открытия файлов (сессий)
Чтобы понять, нужно форкать сейчас или нет, нужно чтобы пол-скрипта проработалось. Заметив такое требование, я отказался от него, дабы исключить потерю своего времени. Но ты всё равно молодец, сколько часов просидел, чтобы узнать это?

>гы, а до этого ты что, форкался из процесса апача?
Не понял вопроса наверное в виду того, что не прочёл инструкцию - буквально и полностью. Но ты то молодец.

>чтобы процесс стал демоном, его надо отключить от консоли >/dev/null 2>&1
>за 5 минут ты в этом не разберешься
/dev/null юзал, ага.

>писать заявление по профнепригодности и менять профессию
Профнепригодность в чём заключается? В незнании всех деталей языка программирования? В отсутствии времени для ознакомления с "инструкцией" ? Много ты знаешь о моих обязанностях, ресурсах и знаниях.

Так что - на %%% это немного туда и налево.
 

whirlwind

TDD infected, paranoid
Если после форка закрываются все дескрипторы, то это баг. После форка все дескрипторы должны дублироваться.
 
Сверху