2013-07-16 2 views
3

Я разработал механизм метапоиска, и одна из оптимизаций, которые я хотел бы сделать, - это параллельно обрабатывать API поиска. Представьте, что результаты получены из поисковой системы A через 0,24 секунды, SE B через 0,45 секунды и от SE C через 0,5 секунды. С другими накладными расходами метапоиска может возвращать агрегированные результаты примерно за 1,5 секунды, что является жизнеспособным. Теперь то, что я хотел бы сделать, - это отправить эти запросы параллельно, а не последовательно, как в настоящее время, и довести это время до секунды. Я исследовал exec, forking, threading и все, по разным причинам, потерпел неудачу. Теперь я потратил день или два на это, чтобы, возможно, что-то пропустил. В идеале я хотел бы реализовать это в стеке WAMP на моей машине разработки (localhost) и посмотреть о внедрении на веб-сервере Linux после этого. Любая помощь оценивается.PHP Параллельная обработка для Metasearch Engine

Давайте рассмотрим простой пример: скажем, у нас есть два файла, которые мы хотим запустить одновременно. Файл 1:

<?php 
// file1.php 
echo 'File 1 - Test 1'.PHP_EOL; 
$sleep = mt_rand(1, 5); 
echo 'Start Time: '.date("g:i:sa").PHP_EOL; 
echo 'Sleep Time: '.$sleep.' seconds.'.PHP_EOL; 
sleep($sleep); 
echo 'Finish Time: '.date("g:i:sa").PHP_EOL; 
?> 

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

File 1 - Test 1 
Start Time: 9:30:43am 
Sleep Time: 4 seconds. 
Finish Time: 9:30:47am 

Но могу ли я использовать exec, popen или что-то еще, я просто не могу заставить это работать на PHP!

+0

Все знаки указывают на 'exec()' –

+0

Давайте рассмотрим простой пример: скажем, у нас есть два файла, которые мы хотим запустить одновременно. Файл 1:

+0

@ConorRyan Проверьте обновленный ответ - я включил рабочий пример. – bbonev

ответ

1

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

В вашем главном приложении вы можете popen как многие из этих рабочих, как вам нужно, а затем в простом цикле сбора выходов:

[править] Я использовал свой пример работник, просто пришлось chmod +x и добавить #!/usr/bin/php линия на вершине:

#!/usr/bin/php 
<?php 
echo 'File 1 - Test 1'.PHP_EOL; 
$sleep = mt_rand(1, 5); 
echo 'Start Time: '.date("g:i:sa").PHP_EOL; 
echo 'Sleep Time: '.$sleep.' seconds.'.PHP_EOL; 
sleep($sleep); 
echo 'Finish Time: '.date("g:i:sa").PHP_EOL; 
?> 

также изменил сценарий запуска немного - ex.php:

#!/usr/bin/php 
<?php 
$pha=array(); 
$res=array(); 
$pha[1]=popen("./file1.php","r"); 
$res[1]=''; 
$pha[2]=popen("./file2.php","r"); 
$res[2]=''; 
while (list($id,$ph)=each($pha)) { 
    while (!feof($ph)) 
     $res[$id].=fread($ph,8192); 
    pclose($ph); 
} 
echo $res[1].$res[2]; 

вот результат при испытании кли (его то же самое, когда ex.php вызывается из сети, но пути к file1.php и file2.php должен быть установлен):

$ time ./ex.php 
File 1 - Test 1 
Start Time: 11:00:33am 
Sleep Time: 3 seconds. 
Finish Time: 11:00:36am 
File 2 - Test 1 
Start Time: 11:00:33am 
Sleep Time: 4 seconds. 
Finish Time: 11:00:37am 

real 0m4.062s 
user 0m0.040s 
sys 0m0.036s 

Как видно из результата, один сценарий занимает 3 секунды для выполнения, а другой - 4. Оба они работают в течение 4 секунд вместе.

[конец редактирования]

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

Наконец, это займет (самое медленное рабочее время) + (время для сбора) для выполнения. Поскольку время для сбора результатов и времени для несериализации и т. Д. Можно игнорировать, вы получаете все данные за время самого медленного запроса.

В качестве побочного примечания вы можете попытаться использовать сериализатор igbinary, который намного быстрее, чем встроенный.

Как было отмечено в комментариях:

worker.php выполняется за пределами веб-запроса, и вы должны передать все свое состояние с помощью аргументов. Передача аргументов также может быть проблемой для обработки всех экранов, безопасности и т. Д., Поэтому неэффективный, но простой способ - использовать base64.

Основным недостатком этого подхода является то, что его отладка непросто.

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

+0

рабочий код не будет работать в том же контексте, что и приложение. Это сделает невозможным немедленное отладка приложения и требует связи требуемого контекста приложения с и с рабочим сценарием. а также обработка исключений и/или ошибок потребует внимания, чтобы заставить его работать (имитируется, используя коды возврата и сообщения -> странно). 'socket_select()' is * the * way to go – hek2mgl

+0

Я подумал об этом. Теперь я думаю, что преимущество 'socket_select()' зависит от времени, необходимого для подключения к серверу. Если это займет много времени, ваше решение может быть лучше. Я должен признать это, чтобы сохранить спортивность. Если вы заинтересованы в готовом к использованию рабочем решении, посмотрите [Gearman] (http://gearman.org/) – hek2mgl

+0

Извините, если мои комментарии были оскорбительными. не считайте это личным, потому что мне нравится 'socket_select() | stream_select()' и думаю, что во многих случаях они хороши. Возьмите свой ответ, например. Вы можете использовать 'stream_select()' для чтения от всех рабочих параллельно (если вы запускаете процесс с помощью 'proc_open()'). Однако, поскольку время соединения является кумулятивным в моем ответе, в отличие от вашего, я бы, наконец, сказал, что ваш лучшее решение. +1 – hek2mgl

1

Я бы использовал socket_select(). Таким образом, только время соединения будет неустойчивым, поскольку вы можете читать из сокетов в parralel. Это даст вам большой повышение производительности.

Смежные вопросы