Параллельный запуск скрипта php

Как лучше реализовать параллельное выполнение скриптов в Php?

Скрипт принимает запрос от пользователя, и нужно параллельно проверить информацию на 30-ти сайтах-поставщиках (число будет расти).
Проанализировать ответ — и отдать пользователю.
И эти 30 запросов необходимо выполнить быстро, параллельно, а не последовательно.

Multi-curl не подходит, т.к. после каждого curl для каждого сайта свои действия, возможно дополнительные curl-запросы, и другие вычисления
По сути это 30 функций, которые надо запустить параллельно, но основное время выполнения занимает ожидание ответа от сайтов, тут еще время ответа увеличивают PROXY.

Какие варианты лучше всего можно использовать в PHP для параллельного запуска?

Чтобы работало быстро и тратило меньше всего ресурсов.

У меня в голове крутятся такие:

1. Реализовать запуском N скриптов, например через shell_exec(«php script.php $params») — без ожидания ответа. И таким образом не иметь возможности обрабатывать ответ от каждого скрипта.

2. Делать с помощью pcntl — и тоже не иметь возможности дождаться и обработать ответ.

3. Сделать с помощью библиотеки pthreads — здесь можно запустить сразу 30 функций в 30-ти потоках( каждая функция в своем потоке) и дождаться их выполнения и обработать результаты.
(В данный момент реализован этот вариант, но мне сказали, что это плохой вариант)

Какой из этих вариантов лучше остальных? В чем отличия между этими вариантами?
И может кто-то какие-то варианты еще подскажет на php?

А также может это стоит реализовать на других языках программирования, и если так, то на каких и с использованием каких инструментов?

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

Простой 1 комментарий

Источник

Введение

parallel — это модуль параллельного выполнения для PHP ≥ 7.2.0. Начиная с parallel 1.2.0, требуется PHP ≥ 8.0.0.

Краткое описание основных понятий параллельных процессов описано ниже, более подробную информацию можно найти в этом разделе руководства.

Runtime

parallel\Runtime представляет поток интерпретатора PHP. parallel\Runtime настроен с дополнительным файлом начальной загрузки, переданным в parallel\Runtime::__construct() , обычно это автозагрузчик или какая-либо другая процедура предварительной загрузки: файл начальной загрузки будет подключаться перед выполнением любой задачи.

Читайте также:  Java optional example orelse

После создания parallel\Runtime остаётся доступным до тех пор, пока он не будет закрыт, уничтожен или уничтожен обычными правилами области видимости объектов PHP. parallel\Runtime::run() позволяет программисту планировать задачи для параллельного выполнения. У parallel\Runtime расписание FIFO, задачи будут выполняться в том порядке, в котором они запланированы.

Функциональный API

parallel реализует функциональный API более высокого уровня поверх parallel\Runtime , который обеспечивает единую точку входа функции для выполнения параллельного кода с автоматическим планированием: parallel\run() .

Задача

Замечание:

Вложенные замыкания могут содержать «yield» или «use by-reference», но не должны содержать объявления классов или именованных функций.

Замечание:

Никакие инструкции не запрещены в файлах, которые может включать задача.

Фьючерс

parallel\Future используется для доступа к возвращаемому значению из задачи и предоставляет API для отмены задачи.

Канал

Задачу можно запланировать с аргументами, использовать переменные лексической области видимости (по значению) и вернуть значение (с помощью parallel\Future ), но это разрешает только однонаправленную связь: Это позволяет программисту отправлять данные и извлекать данные из задачи, но не допускают двунаправленную связь между задачами. API parallel\Channel обеспечивает двунаправленную связь между задачами, parallel\Channel — это соединение типа сокета между задачами, которое программист может использовать для отправки и получения данных.

События

API parallel\Events реализует собственный цикл событий ( Traversable ) и метод parallel\Events::poll() . Это позволяет программисту работать с наборами каналов и/или фьючерсами. Программист просто добавляет каналы и фьючерсы в цикл событий, необязательно устанавливая вход для записи с помощью parallel\Events::setInput() , и входит в foreach: parallel будет читать и записывать объекты по мере их превращения. Доступно с получением объектов parallel\Events\Event , описывающих произошедшие операции.

Источник

Параллелим процесс на PHP

Давно уже наткнулся на одну статью, автор которой приводит пример распараллеливания работы php-скриптов через сокеты, но некогда было ее прочитать. Ссылку на эту статью можете увидеть в конце поста. Вот, наконец, добрался до нее и после прочтения возникло дикое желание распараллелить всем известный пример вычисления числа PI на MPI. У меня двуядерная машинка, поэтому эффект от распараллеливания должен проявиться.

Читайте также:  Html css style tabs

В итоге, переработав пример из статьи про распараллеливание, получилось два скрипта – test.php и pi.php. Первый скрипт является главным и управляет работой второго. Второй скрипт отвечает за вычисление числа PI, т.е. именно он запускается параллельно.

 $value) < $_post[] = $name.'='.urlencode($value); >> $post = implode('&', $_post); stream_set_blocking($fp, false); stream_set_timeout($fp, 86400); // вызываем скрипт, передавая ему необходимые переменные fwrite($fp, "POST /$uri HTTP/1.1\r\nHost: $server \r\n". "Content-Type: application/x-www-form-urlencoded\r\n". "Content-Length: ".strlen($post)."\r\n". "Connection: close\r\n\r\n$post" ); return $fp; > // эта функция берет вывод параллельного скрипта и возвращает его function GetTaskOutput(&$fp) < if ($fp === false) < return false; >if (feof($fp)) < fclose($fp); $fp = false; return false; >return fread($fp, 512); > // функция для вычисления времени (чтобы замерить время выполнения кода) function GetMTime() < $mtime = microtime(); $mtime = explode(' ', $mtime); $mtime = $mtime[1] + $mtime[0]; return $mtime; >set_time_limit(0); // это обязательно должно быть нулем, иначе настройки php.ini могут не позволить скрипту работать долго // Запускаем сначала для одного потока 5 экспериментов, потом - для двух потоков. И посмотрим, какое будет время. $n = 40000000; // кол-во интервалов $PI25DT = 3.141592653589793238462643; // точное значение числа PI for ($size = 1; $size $i, 'size' => $size, 'n' => $n)); > $mypi = 0; while (true) < $out = Array(); $break = true; for ($i = 0; $i < $size; $i++) < $out[$i] = GetTaskOutput($fp[$i]); if ($out[$i] !== false) < $break = false; >> if ($break) < break; >// выделяем строку результата из вывода скрипта. Почему так? Попробуйте без этого и будет понятно for ($i = 0; $i < $size; $i++) < if (preg_match('!(.*)?!is', $out[$i], $matches) > 0) < $mypi += $matches[1]; >> > $time2 = GetMTime(); if ($time2 - $time1 < $minTime || $k == 0) < $minTime = $time2 - $time1; >> echo '
'; echo 'Size: '.$size.'
'; // выводим кол-во скриптов, параллельно работавших echo 'N: '.$n.'
'; echo 'Exact PI = '.number_format($PI25DT, 16).'
'; echo 'My PI = '.number_format($mypi, 16).'
'; // выводим вычисленное значение числа PI echo 'Time, sec: '.$minTime; // минимальное время работы в течение всех экспериментов echo '

'; flush(); @ob_flush(); > ?>

Я не буду говорить о методах вычисления числа PI в этом посте, т.к. это тема отдельного разговора. Я просто посмотрел на реализацию параллельного алгоритма вычисления PI в примерах библиотеки MPI и сделал то же самое, но на PHP.

Да, тут в примере я провожу сначала 5 экспериментов для одного процесса, потом те же 5 экспериментов, но для двух параллельных процессов. Т.е. в сумме 10 экспериментов – 2 итерации по 5 итераций в каждой.

Тут конечно самое интересное – а действительно ли будет выигрыш от распараллеливания? Я решил проверить, и вот что у меня получилось:

Size: 1
N: 40000000
Exact PI = 3.1415926535897931
My PI = 3.1415926535888001
Time, sec: 22.361926078796

Size: 2
N: 40000000
Exact PI = 3.1415926535897931
My PI = 3.1415926535901999
Time, sec: 15.371038913727

:)

Первый результат – для 40 миллионов интервалов и одного процесса. Второй результат – то же самое, но уже распараллелено на два процесса. Как видим, погрешность во втором случае чуть меньше, чем в первом. Но самое интересное – выигрыш есть, и он оказался 30-ти процентным. Неплохой результат однако В идеале конечно должно быть 50%, но такого не бывает никогда, т.к. очень много посторонних факторов влияет на процессы и процессоры.

:)

Почему я брал минимальное время после проведения каждых 5 экспериментов? Почему не среднее? Дело в том, что так правильнее Минимальное время показывает, что на такой итерации с таким временем процессор был меньше загружен всяким мусором (работающими на фоне программами и т.п.). А если бы мы вычисляли среднее время – мы бы брали в расчет и мусор, который нескромно влияет на результат.

:)

Я, конечно, мог бы делать не 5 итераций, а, скажем, 50, но тогда пришлось бы ждать более получаса, чтобы процессы отработали Если кому интересно – можете и на сотне итераций пробовать :))

Чем больше итераций, тем точнее результат.

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

:)

Вычисление PI – это пример, первый пришедший в голову, и сразу пошедший в реализацию На самом деле можно много чего придумать. Приведенный выше код можно подточить под собственные нужды.

А вот и сама ссылка на первоисточник: [ссылка]

Источник

Оцените статью