Estou trabalhando em um aplicativo da web PHP e preciso executar algumas operações de rede na solicitação, como buscar alguém no servidor remoto com base na solicitação do usuário.
É possível simular o comportamento assíncrono em PHP, visto que tenho que passar alguns dados para uma função e também preciso de saída dela.
Meu código é como:
<?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;
?>
Cada operação de rede leva cerca de 5 segundos para ser concluída, adicionando um total de 15 segundos ao tempo de resposta do meu aplicativo, desde que eu faça 3 solicitações.
A função makeNetworkCall () apenas faz uma solicitação HTTP POST.
O servidor remoto é uma API de terceiros, então não tenho nenhum controle sobre ele.
PS: Por favor, não responda dando sugestões sobre AJAX ou outras coisas. Atualmente, estou procurando se posso fazer isso através do PHP pode ser com uma extensão C ++ ou algo parecido.
fonte
CURL
para disparar solicitações e buscar alguns dados da web ...Respostas:
Hoje em dia, é melhor usar filas do que threads (para quem não usa o Laravel, existem toneladas de outras implementações como essa ).
A ideia básica é que seu script PHP original coloca tarefas ou trabalhos em uma fila. Então, você tem trabalhadores de tarefa de fila em execução em outro lugar, tirando tarefas da fila e começa a processá-los independentemente do PHP original.
As vantagens são:
fonte
Não tenho uma resposta direta, mas você pode querer investigar estas coisas:
job
feito isso, inserir uma nova tarefa descrevendo o trabalho que precisa ser feito para processar o corpo da resposta HTTP em cache.fonte
cURL vai ser sua única escolha real aqui (ou isso, ou usando soquetes sem bloqueio e alguma lógica personalizada).
Este link deve encaminhá-lo na direção certa. Não há processamento assíncrono em PHP, mas se você estiver tentando fazer várias solicitações da web simultâneas, cURL multi cuidará disso para você.
fonte
Acho que se o HTML e outras coisas da interface do usuário precisam dos dados retornados, não haverá uma maneira de assíncroná-los.
Acredito que a única maneira de fazer isso em PHP seria registrar uma solicitação em um banco de dados e ter uma verificação cron a cada minuto, ou usar algo como processamento de fila Gearman, ou talvez exec () um processo de linha de comando
Nesse ínterim, sua página php teria que gerar algum html ou js que o faz recarregar a cada poucos segundos para verificar o progresso, o que não é o ideal.
Para contornar o problema, quantas solicitações diferentes você está esperando? Você poderia baixar todos eles automaticamente a cada hora ou assim e salvá-los em um banco de dados?
fonte
Também existe o http v2, que é um wrapper para o curl. Pode ser instalado via pecl.
http://devel-m6w6.rhcloud.com/mdref/http/
fonte
Acho que algum código sobre a solução cURL é necessário aqui, então vou compartilhar o meu (ele foi escrito misturando várias fontes como o Manual do PHP e comentários).
Ele faz algumas solicitações HTTP paralelas (domínios em
$aURLs
) e imprime as respostas assim que cada uma é concluída (e as armazena$done
para outros usos possíveis).O código é mais longo do que o necessário devido à parte impressa em tempo real e ao excesso de comentários, mas fique à vontade para editar a resposta para melhorá-la:
<?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 ?>
fonte
Uma maneira é usar
pcntl_fork()
em uma função recursiva.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);
Uma coisa
pcntl_fork()
é que, ao executar o script por meio do Apache, ele não funciona (não é suportado pelo Apache). Então, uma maneira de resolver esse problema é rodar o script usando o php cli, como:exec('php fork.php',$output);
de outro arquivo. Para fazer isso, você terá dois arquivos: um que é carregado pelo Apache e outro que é executadoexec()
de dentro do arquivo carregado pelo Apache assim: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);
fonte