PHP'de Eşzamansız İşlev Çağrısı


86

Bir PHP web uygulaması üzerinde çalışıyorum ve kullanıcının isteğine göre uzak sunucudan birini almak gibi istekte bazı ağ işlemleri gerçekleştirmem gerekiyor.

Bazı verileri bir işleve iletmem ve ayrıca ondan çıktıya ihtiyacım olduğu için PHP'de eşzamansız davranışı simüle etmek mümkün müdür?

Kodum şöyle:

<?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;
?>

Her ağ işleminin tamamlanması yaklaşık 5 saniye sürüyor ve başvurumun yanıt süresine toplam 15 saniye ekledim.

MakeNetworkCall () işlevi yalnızca bir HTTP POST isteği yapar.

Uzak sunucu bir 3. parti API olduğu için orada herhangi bir kontrolüm yok.

Not: Lütfen AJAX veya diğer konularla ilgili önerilerinizi yanıtlamayın. Şu anda bakıyorum bunu PHP aracılığıyla yapıp yapamayacağımı bir C ++ uzantısı veya bunun gibi bir şey olabilir.


CURLİstekleri ateşlemek ve web'den bazı veriler almak için kullanmayı deneyin ...
Bogdan Burym

Cevabın burada olduğuna inanıyorum: stackoverflow.com/questions/13846192/… Hızlı not: iş parçacığı kullanın
DRAX


Engellemeyen kodu çalıştırmak için PHP'nin stream_select işlevini kullanabilirsiniz. React , node.js'ye benzer olay odaklı bir döngü oluşturmak için bunu kullanır .
Quinn Comendant

Yanıtlar:


20

Günümüzde, kullanmak daha iyidir sıraları parçacığı daha (laravel kullanmayan kişiler için orada diğer uygulamaların ton vardır böyle ).

Temel fikir, orijinal PHP betiğinizin görevleri veya işleri bir sıraya koymasıdır. Ardından, başka bir yerde çalışan, işleri kuyruktan çıkaran ve bunları orijinal PHP'den bağımsız olarak işlemeye başlayan kuyruk işçileri var.

Avantajlar:

  1. Ölçeklenebilirlik - talebe ayak uydurmak için yalnızca çalışan düğümleri ekleyebilirsiniz. Bu şekilde görevler paralel olarak yürütülür.
  2. Güvenilirlik - RabbitMQ, ZeroMQ, Redis, vb. Gibi modern kuyruk yöneticileri son derece güvenilir olacak şekilde yapılmıştır.


8

Doğrudan bir cevabım yok, ancak şunlara bakmak isteyebilirsiniz:


3

cURL buradaki tek gerçek seçiminiz olacak (ya bu ya da engellemeyen soketler ve bazı özel mantık kullanarak).

Bu bağlantı sizi doğru yöne göndermelidir. PHP'de eşzamansız işlem yoktur, ancak aynı anda birden fazla web isteği yapmaya çalışıyorsanız, cURL multi bunu sizin için halleder.


2

Bence HTML ve diğer UI öğeleri döndürülen verilere ihtiyaç duyarsa, onu eşzamansız hale getirmenin bir yolu olmayacak.

Bunu PHP'de yapmanın tek yolunun bir veritabanına bir istek kaydetmek ve her dakika bir cron kontrolü yapmak veya Gearman kuyruk işleme gibi bir şey veya belki bir komut satırı işlemi kullanmak olduğuna inanıyorum.

Bu arada, php sayfanızın ilerlemeyi kontrol etmek için birkaç saniyede bir yeniden yüklenmesini sağlayan bazı html veya js üretmesi gerekir, ideal değil.

Sorunun üstesinden gelmek için kaç farklı istek bekliyorsunuz? Hepsini her saat otomatik olarak indirip bir veritabanına kaydedebilir misiniz?


1

Bu eski sorunun yeni bir cevabı var. Bugünlerde PHP için birkaç "eşzamansız" çözüm var (bunlar, çerçeve düzeyinde yönetmek yerine yeni bağımsız PHP süreçleri ortaya çıkardıkları için Python'un çoklu işlemine eşdeğerdir)

Gördüğüm iki çözüm

Onlara bir şans verin!



0

Burada cURL çözümüyle ilgili bazı koda ihtiyaç olduğunu düşünüyorum, bu yüzden benimkini paylaşacağım (PHP Kılavuzu ve yorumlar olarak birkaç kaynağı karıştırarak yazılmıştır).

Bazı paralel HTTP istekleri yapar (etki alanları $aURLs) ve her biri tamamlandıktan sonra yanıtları yazdırır (ve $donediğer olası kullanımlar için depolanır ).

Kod gerekenden daha uzun çünkü gerçek zamanlı yazdırma kısmı ve fazla yorum var, ancak iyileştirmek için yanıtı düzenlemekten çekinmeyin:

<?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
?>

0

Bir yol, pcntl_fork()özyinelemeli bir işlevde kullanmaktır .

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);

Bir şey hakkında pcntl_fork()Apache yoluyla komut çalıştırırken, o (o Apache tarafından desteklenmeyen) işi değil yapmasıdır. Bu nedenle, bu sorunu çözmenin bir yolu, betiği php cli kullanarak çalıştırmaktır, örneğin: exec('php fork.php',$output);başka bir dosyadan. Bunu yapmak için iki dosyanız olacak: biri Apache tarafından yüklenen ve diğeri Apache tarafından şu şekilde yüklenen exec()dosyanın içinden çalıştırılan :

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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.