Ответ 1
Я считаю, что ответ лежит здесь:
Астрономический вызов PHP для php-функции
Быстрая заметка: используйте threading
Я работаю над веб-приложением PHP, и мне нужно выполнить некоторые сетевые операции в запросе, например, выбор кого-то из удаленного сервера на основе пользовательского запроса.
Можно ли моделировать асинхронное поведение в PHP, учитывая, что мне нужно передать некоторые данные функции, а также получить вывод из нее.
Мой код похож:
<?php
$data1 = processGETandPOST();
$data2 = processGETandPOST();
$data3 = processGETandPOST();
$response1 = makeNetworkCall($data1);
$response2 = makeNetworkCall($data2);
$response3 = makeNetworkCall($data3);
processNetworkResponse($response1);
processNetworkResponse($response2);
processNetworkResponse($response3);
/*HTML and OTHER UI STUFF HERE*/
exit;
?>
Каждая операция сети занимает около 5 секунд, чтобы завершить добавление в общей сложности 15 секунд к времени отклика моего приложения, если я сделаю 3 запроса.
Функция makeNetworkCall() выполняет запрос HTTP POST.
Удаленный сервер является сторонним API, поэтому я не могу контролировать его.
PS: Пожалуйста, не отвечайте на предложения о AJAX или других вещах. В настоящее время я смотрю, могу ли я это сделать через PHP, может быть с расширением С++ или что-то в этом роде.
Я считаю, что ответ лежит здесь:
Астрономический вызов PHP для php-функции
Быстрая заметка: используйте threading
В настоящее время лучше использовать очереди, чем потоки (для тех, кто не использует Laravel, существует множество других реализаций, подобных этой).
Основная идея в том, что ваш оригинальный скрипт PHP помещает задачи или задания в очередь. Затем у вас есть работники очереди, работающие в другом месте, вынимающие задания из очереди и начинающие обрабатывать их независимо от исходного PHP.
Преимущества:
У меня нет прямого ответа, но вы можете посмотреть на эти вещи:
job
будет выполнено, вставьте новое задание, описывающее работу, которую необходимо выполнить для обработки кэшированного тела HTTP-ответа.cURL будет вашим единственным реальным выбором здесь (или с использованием неблокирующих сокетов и некоторой пользовательской логики).
Эта ссылка должна отправить вас в правильном направлении. В PHP нет асинхронной обработки, но если вы пытаетесь сделать несколько одновременных веб-запросов, cURL multi позаботится об этом для вас.
Я думаю, что если HTML и другие элементы пользовательского интерфейса нуждаются в данных, возвращаемых, то не будет способа их асинхронности.
Я считаю, что единственный способ сделать это в PHP - это зарегистрировать запрос в базе данных и ежеминутно проверять cron, или использовать что-то вроде обработки очереди Gearman, или, может быть, exec() процесс командной строки
Тем временем вам на php-страницу нужно будет генерировать некоторые html или js, которые заставляют его перезагружать каждые несколько секунд, чтобы проверить прогресс, а не идеально.
Чтобы обойти проблему, сколько разных ожидаемых запросов вы ожидаете? Не могли бы вы загрузить их автоматически каждый час или около того и сохранить в базе данных?
Существует также http v2, который является оберткой для завитки. Может быть установлен через pecl.
Я думаю, что здесь нужен некоторый код о решении cURL, поэтому я поделюсь своим (он был написан с использованием нескольких источников, таких как Руководство по PHP и комментарии).
Он выполняет несколько параллельных HTTP-запросов (домены в $aURLs
) и печатает ответы после завершения каждого из них (и сохраняет их в $done
для других возможных применений).
Код длиннее, чем нужно, потому что часть печати в реальном времени и избыток комментариев, но не стесняйтесь редактировать ответ, чтобы улучшить его:
<?php
/* Strategies to avoid output buffering, ignore the block if you don't want to print the responses before every cURL is completed */
ini_set('output_buffering', 'off'); // Turn off output buffering
ini_set('zlib.output_compression', false); // Turn off PHP output compression
//Flush (send) the output buffer and turn off output buffering
ob_end_flush(); while (@ob_end_flush());
apache_setenv('no-gzip', true); //prevent apache from buffering it for deflate/gzip
ini_set('zlib.output_compression', false);
header("Content-type: text/plain"); //Remove to use HTML
ini_set('implicit_flush', true); // Implicitly flush the buffer(s)
ob_implicit_flush(true);
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
$string=''; for($i=0;$i<1000;++$i){$string.=' ';} output($string); //Safari and Internet Explorer have an internal 1K buffer.
//Here starts the program output
function output($string){
ob_start();
echo $string;
if(ob_get_level()>0) ob_flush();
ob_end_clean(); // clears buffer and closes buffering
flush();
}
function multiprint($aCurlHandles,$print=true){
global $done;
// iterate through the handles and get your content
foreach($aCurlHandles as $url=>$ch){
if(!isset($done[$url])){ //only check for unready responses
$html = curl_multi_getcontent($ch); //get the content
if($html){
$done[$url]=$html;
if($print) output("$html".PHP_EOL);
}
}
}
};
function full_curl_multi_exec($mh, &$still_running) {
do {
$rv = curl_multi_exec($mh, $still_running); //execute the handles
} while ($rv == CURLM_CALL_MULTI_PERFORM); //CURLM_CALL_MULTI_PERFORM means you should call curl_multi_exec() again because there is still data available for processing
return $rv;
}
set_time_limit(60); //Max execution time 1 minute
$aURLs = array("http://domain/script1.php","http://domain/script2.php"); // array of URLs
$done=array(); //Responses of each URL
//Initialization
$aCurlHandles = array(); // create an array for the individual curl handles
$mh = curl_multi_init(); // init the curl Multi and returns a new cURL multi handle
foreach ($aURLs as $id=>$url) { //add the handles for each url
$ch = curl_init(); // init curl, and then setup your options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // returns the result - very important
curl_setopt($ch, CURLOPT_HEADER, 0); // no headers in the output
$aCurlHandles[$url] = $ch;
curl_multi_add_handle($mh,$ch);
}
//Process
$active = null; //the number of individual handles it is currently working with
$mrc=full_curl_multi_exec($mh, $active);
//As long as there are active connections and everything looks OK…
while($active && $mrc == CURLM_OK) { //CURLM_OK means is that there is more data available, but it hasn't arrived yet.
// Wait for activity on any curl-connection and if the network socket has some data…
if($descriptions=curl_multi_select($mh,1) != -1) {//If waiting for activity on any curl_multi connection has no failures (1 second timeout)
usleep(500); //Adjust this wait to your needs
//Process the data for as long as the system tells us to keep getting it
$mrc=full_curl_multi_exec($mh, $active);
//output("Still active processes: $active".PHP_EOL);
//Printing each response once it is ready
multiprint($aCurlHandles);
}
}
//Printing all the responses at the end
//multiprint($aCurlHandles,false);
//Finalize
foreach ($aCurlHandles as $url=>$ch) {
curl_multi_remove_handle($mh, $ch); // remove the handle (assuming you are done with it);
}
curl_multi_close($mh); // close the curl multi handler
?>