web-dev-qa-db-fra.com

Comment effectuer plusieurs requêtes Guzzle en même temps?

Je peux effectuer des requêtes uniques en utilisant Guzzle et je suis très satisfait des performances de Guzzle jusqu'à présent cependant, j'ai lu dans l'API Guzzle quelque chose sur MultiCurl et Batching.

Quelqu'un pourrait-il m'expliquer comment faire plusieurs demandes en même temps? Async si possible. Je ne sais pas si c'est ce qu'ils signifient avec MultiCurl. La synchronisation ne serait pas non plus un problème. Je veux juste faire plusieurs demandes en même temps ou très près (peu de temps).

22
Martijn

À partir des documents: http://guzzle3.readthedocs.org/http-client/client.html#sending-requests-in-parallel

Pour une solution facile à utiliser qui renvoie un hachage de mappage d'objets de demande à une réponse ou une erreur, voir http://guzzle3.readthedocs.org/batching/batching.html#batching

Petit exemple:

<?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')
));
22
Michael Dowling

Une mise à jour relative au nouveau GuzzleHttp guzzlehttp/guzzle

Les appels simultanés/parallèles sont maintenant exécutés à travers plusieurs méthodes différentes, y compris les promesses .. demandes simultanées

L'ancienne façon de passer un tableau de RequestInterfaces ne fonctionnera plus.

Voir l'exemple ici

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

Mise à jour : comme suggéré dans les commentaires et demandé par @ sankalp-tambe, vous pouvez également utiliser une approche différente pour éviter qu'un ensemble de requêtes simultanées avec un échec ne renverra pas toutes les réponses.

Bien que les options suggérées avec Pool soient réalisables, je préfère toujours les promesses.

Un exemple avec des promesses est d'utiliser des méthodes de règlement et d'attente au lieu de déballer.

La différence avec l'exemple ci-dessus serait

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

J'ai créé un exemple complet ci-dessous pour référence sur la façon de gérer les réponses $ aussi.

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
}

Cet exemple de code a été initialement publié ici .

23
Bizmate

Guzzle 6.0 a rendu l'envoi de plusieurs demandes asynchrones très facile.

Il existe plusieurs façons de procéder.

Vous pouvez créer les demandes asynchrones et ajouter les promesses résultantes à un seul tableau, et obtenir le résultat en utilisant la méthode settle() comme ceci:

$promise1 = $client->getAsync('http://www.example.com/foo1');
$promise2 = $client->getAsync('http://www.example.com/foo2');
$promises = [$promise1, $promise2];

$results = GuzzleHttp\Promise\settle($promises)->wait();

Vous pouvez maintenant parcourir ces résultats et récupérer la réponse à l'aide de GuzzleHttpPromiseall ou GuzzleHttpPromiseeach. Reportez-vous à cet article pour plus de détails.

Si vous avez un nombre indéterminé de demandes à envoyer (disons 5 ici), vous pouvez utiliser GuzzleHttp/Pool::batch(). Voici un exemple:

$client = new Client();

// Create the requests
$requests = function ($total) use($client) {
    for ($i = 1; $i <= $total; $i++) {
        yield new Request('GET', 'http://www.example.com/foo' . $i);
    }
};

// Use the Pool::batch()
$pool_batch = Pool::batch($client, $requests(5));
foreach ($pool_batch as $pool => $res) {

    if ($res instanceof RequestException) {
        // Do sth
        continue;
    }

    // Do sth
}
0
Annapurna