Pracuję nad aplikacją internetową PHP i muszę wykonać pewne operacje sieciowe w żądaniu, takie jak pobranie kogoś ze zdalnego serwera na podstawie żądania użytkownika.
Czy można symulować zachowanie asynchroniczne w PHP, biorąc pod uwagę, że muszę przekazać pewne dane do funkcji, a także potrzebuję danych wyjściowych z niej.
Mój kod jest taki:
<?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;
?>
Każda operacja sieciowa trwa około 5 sekund, dodając łącznie 15 sekund do czasu odpowiedzi mojej aplikacji, biorąc pod uwagę, że wykonuję 3 żądania.
Funkcja makeNetworkCall () po prostu wykonuje żądanie HTTP POST.
Serwer zdalny jest interfejsem API innej firmy, więc nie mam nad nim żadnej kontroli.
PS: Proszę nie odpowiadać, podając sugestie dotyczące AJAX lub innych rzeczy. Obecnie szukam, czy mogę to zrobić za pomocą PHP, może być z rozszerzeniem C ++ lub czymś podobnym.
źródło
CURL
do odpalenia żądań i pobrania danych z sieci ...Odpowiedzi:
W dzisiejszych czasach lepiej jest używać kolejek niż wątków (dla tych, którzy nie używają Laravel, istnieje mnóstwo innych implementacji, takich jak ta ).
Podstawowa idea jest taka, że Twój oryginalny skrypt PHP umieszcza zadania lub zadania w kolejce. Następnie pracownicy zadań kolejki działają w innym miejscu, usuwają zadania z kolejki i rozpoczynają ich przetwarzanie niezależnie od oryginalnego PHP.
Zalety to:
źródło
Nie mam bezpośredniej odpowiedzi, ale warto przyjrzeć się tym rzeczom:
job
to zrobisz, wstaw nowe zadanie opisujące pracę, jaką należy wykonać, aby przetworzyć treść odpowiedzi HTTP w pamięci podręcznej.źródło
cURL będzie tutaj jedynym prawdziwym wyborem (albo to, albo używając nieblokujących gniazd i niestandardowej logiki).
Ten link powinien skierować cię we właściwym kierunku. W PHP nie ma przetwarzania asynchronicznego, ale jeśli próbujesz wykonać wiele jednoczesnych żądań internetowych, cURL multi zajmie się tym za Ciebie.
źródło
Myślę, że jeśli HTML i inne elementy interfejsu użytkownika wymagają zwrócenia danych, nie będzie sposobu na ich asynchronizację.
Uważam, że jedynym sposobem na zrobienie tego w PHP byłoby zarejestrowanie żądania w bazie danych i sprawdzanie crona co minutę lub użycie czegoś takiego jak przetwarzanie kolejki Gearmana lub może exec () proces wiersza poleceń
W międzyczasie strona php musiałaby wygenerować trochę html lub js, co powoduje, że ładuje się co kilka sekund, aby sprawdzić postęp, nie jest to idealne.
Aby obejść ten problem, ile różnych żądań się spodziewasz? Czy możesz je wszystkie automatycznie pobierać mniej więcej co godzinę i zapisywać w bazie danych?
źródło
Jest też http v2, który jest opakowaniem dla curl. Można zainstalować za pomocą pecl.
http://devel-m6w6.rhcloud.com/mdref/http/
źródło
Myślę, że potrzebny jest tutaj kod dotyczący rozwiązania cURL, więc podzielę się moim (został napisany z mieszaniem kilku źródeł, takich jak podręcznik PHP i komentarze).
Wykonuje kilka równoległych żądań HTTP (w domenie
$aURLs
) i drukuje odpowiedzi po zakończeniu każdego z nich (i zapisuje je$done
do innych możliwych zastosowań).Kod jest dłuższy niż potrzeba, ponieważ część do drukowania w czasie rzeczywistym i nadmiar komentarzy, ale możesz edytować odpowiedź, aby ją ulepszyć:
<?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 ?>
źródło
Jednym ze sposobów jest użycie
pcntl_fork()
w funkcji rekurencyjnej.function networkCall(){ $data = processGETandPOST(); $response = makeNetworkCall($data); processNetworkResponse($response); return true; } function runAsync($times){ $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent $times -= 1; if($times>0) runAsync($times); pcntl_wait($status); //Protect against Zombie children } else { // we are the child networkCall(); posix_kill(getmypid(), SIGKILL); } } runAsync(3);
Jedną z rzeczy
pcntl_fork()
jest to, że podczas uruchamiania skryptu przez Apache, nie działa (nie jest obsługiwany przez Apache). Tak więc jednym ze sposobów rozwiązania tego problemu jest uruchomienie skryptu przy użyciu cli php, na przykład:exec('php fork.php',$output);
z innego pliku. Aby to zrobić, będziesz mieć dwa pliki: jeden załadowany przez Apache i jeden uruchamiany zexec()
wnętrza pliku załadowanego przez Apache w następujący sposób:apacheLoadedFile.php
exec('php fork.php',$output);
fork.php
function networkCall(){ $data = processGETandPOST(); $response = makeNetworkCall($data); processNetworkResponse($response); return true; } function runAsync($times){ $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent $times -= 1; if($times>0) runAsync($times); pcntl_wait($status); //Protect against Zombie children } else { // we are the child networkCall(); posix_kill(getmypid(), SIGKILL); } } runAsync(3);
źródło