2013-10-22 9 views
16

Ich kann einzelne Anfragen mit Guzzle ausführen und ich bin sehr zufrieden mit Guzzles Leistung, aber ich habe in der Guzzle API etwas über MultiCurl und Batching gelesen.Wie werden mehrere Guzzle-Anfragen gleichzeitig ausgeführt?

Kann mir jemand erklären, wie man mehrere Anfragen gleichzeitig macht? Asynchron, wenn möglich. Ich weiß nicht, ob sie das mit MultiCurl meinen. Sync wäre auch kein Problem. Ich möchte einfach mehrere Anfragen gleichzeitig oder sehr kurz (kurze Zeit) erledigen.

+0

Es gibt eine [Demo von diesem] (http://guzzphp.org/http-client/client.html#sending-requests-in-parallel) in den Dokumenten. Dies ist aus Ihrer Sicht immer noch ein synchroner Aufruf, wird aber intern parallel sein - daher ist die Gesamtzeit für den Aufruf nur die Zeit für den einzelnen längsten Abruf. – halfer

Antwort

18

Aus der Dokumentation: http://guzzle3.readthedocs.org/http-client/client.html#sending-requests-in-parallel

Für eine einfache Lösung zu verwenden, die eine Hash-Anforderung an eine Antwort oder Fehler Objekte Mapping zurückgibt, siehe http://guzzle3.readthedocs.org/batching/batching.html#batching

kurzes Beispiel:

<?php 

$client->send(array(
    $client->get('http://www.example.com/foo'), 
    $client->get('http://www.example.com/baz'), 
    $client->get('http://www.example.com/bar') 
)); 
16

Ein Update im Zusammenhang mit dem neuen GuzzleHttp guzzlehttp/guzzle

Gleichzeitige/parallele Aufrufe werden jetzt durch ein paar verschiedene Methoden einschließlich Promises ausgeführt. Concurrent Requests

Die alte Methode, ein Array von RequestInterfaces zu übergeben, wird nicht mehr funktionieren.

Siehe Beispiel hier

$newClient = new \GuzzleHttp\Client(['base_uri' => $base]); 
    foreach($documents->documents as $doc){ 

     $params = [ 
      'language' =>'eng', 
      'text' => $doc->summary, 
      'apikey' => $key 
     ]; 

     $requestArr[$doc->reference] = $newClient->getAsync('/1/api/sync/analyze/v1?' . http_build_query($params)); 
    } 

    $time_start = microtime(true); 
    $responses = \GuzzleHttp\Promise\unwrap($requestArr); //$newClient->send($requestArr); 
    $time_end = microtime(true); 
    $this->get('logger')->error(' NewsPerf Dev: took ' . ($time_end - $time_start)); 

aktualisieren: Wie in den Kommentaren vorgeschlagen und bat um @ sankalp-Tambe, können Sie auch einen anderen Ansatz verwenden, um zu vermeiden, dass eine Reihe von gleichzeitiger Anfrage mit einem Fehler werden nicht alle Antworten zurückgeben.

Während die mit Pool vorgeschlagenen Optionen machbar sind, bevorzuge ich immer noch Versprechungen.

Ein Beispiel mit Versprechungen ist die Verwendung von Methoden settle und and wait statt unwrap.

Die Differenz aus dem obigen Beispiel

$responses = \GuzzleHttp\Promise\settle($requestArr)->wait(); 

Ich habe ein vollständiges Beispiel wäre unten als Referenz erstellt, wie auch die $ Antworten zu behandeln.

require __DIR__ . '/vendor/autoload.php'; 
use GuzzleHttp\Client as GuzzleClient; 
use GuzzleHttp\Promise as GuzzlePromise; 

$client = new GuzzleClient(['timeout' => 12.0]); // see how i set a timeout 
$requestPromises = []; 
$sitesArray = SiteEntity->getAll(); // returns an array with objects that contain a domain 

foreach ($sitesArray as $site) { 
    $requestPromises[$site->getDomain()] = $client->getAsync('http://' . $site->getDomain()); 
} 

$results = GuzzlePromise\settle($requestPromises)->wait(); 

foreach ($results as $domain => $result) { 
    $site = $sitesArray[$domain]; 
    $this->logger->info('Crawler FetchHomePages: domain check ' . $domain); 

    if ($result['state'] === 'fulfilled') { 
     $response = $result['value']; 
     if ($response->getStatusCode() == 200) { 
      $site->setHtml($response->getBody()); 
     } else { 
      $site->setHtml($response->getStatusCode()); 
     } 
    } else if ($result['state'] === 'rejected') { 
     // notice that if call fails guzzle returns is as state rejected with a reason. 

     $site->setHtml('ERR: ' . $result['reason']); 
    } else { 
     $site->setHtml('ERR: unknown exception '); 
     $this->logger->err('Crawler FetchHomePages: unknown fetch fail domain: ' . $domain); 
    } 

    $this->entityManager->persist($site); // this is a call to Doctrines entity manager 
} 

Dieser Beispielcode wurde ursprünglich veröffentlicht here.

+0

Während dies für meinen Anwendungsfall des Ladens mehrerer Bild-URL-Daten auf einmal funktioniert, wie kann man eine Situation handhaben, in der eine der URLs, die geladen werden, einen 404-Fehler wirft? Wenn das passiert, flippt Guzzle aus und wirft eine Guzzle Exception. Ich kann die Verfügbarkeit der URLs nicht garantieren, also hoffte ich, nur mehrere Anfragen zu laden und diejenigen zu verwenden, die tatsächlich durchgehen. – georaldc

+0

Nevermind, ich habe meinen Code neu geschrieben, um stattdessen GuzzleHttp \ Pool zu verwenden. Scheint gut zu funktionieren und gibt mir ein bisschen mehr Kontrolle. – georaldc

+1

können Sie Ihren Code für mehrere URLs mit Pool teilen. –