Search code examples
phpguzzleguzzle6

How to perform concurrent requests with GuzzleHttp


How do I use Guzzle 6 to create 5 async requests with the following conditions:

  • All requests start at the same time
  • I want a 500ms timeout value for all requests. If a request times out I DONT want it to interrupt other requests
  • If a request returns non-200 I DONT want it to interrupt other requests.
  • All requests are on different domains... (so I'm not sure how that fits in with the base_uri setting...

If all 5 requests return 200OK < 500ms then I want to be able to loop through their responses...

BUT, if say 2 of them have non-200 and 1 of them times out (over 500ms), I want to still be able to access the responses for the 2 successful ones.

EDIT So far everything works except timeouts are still raising an exception

Here is what I had so far:

<?php

  require __DIR__.'/../vendor/autoload.php';

  use GuzzleHttp\Client;
  use GuzzleHttp\Promise;

  $client = new Client([
    'http_errors'     => false,
    'connect_timeout' => 1.50, //////////////// 0.50
    'timeout'         => 2.00, //////////////// 1.00
    'headers' => [
      'User-Agent' => 'Test/1.0'
    ]
  ]);

  // initiate each request but do not block
  $promises = [
    'success'            => $client->getAsync('https://httpbin.org/get'),
    'success'            => $client->getAsync('https://httpbin.org/delay/1'),
    'failconnecttimeout' => $client->getAsync('https://httpbin.org/delay/2'),
    'fail500'            => $client->getAsync('https://httpbin.org/status/500'),
  ];

  // wait on all of the requests to complete. Throws a ConnectException if any
  // of the requests fail
  $results = Promise\unwrap($promises);

  // wait for the requests to complete, even if some of them fail
  $results = Promise\settle($promises)->wait();

Solution

  • Guzzle provides fulfilled and rejected callabcks in the pool. here I performed a test by your values, read more at Guzzle docs:

        $client = new Client([
            'http_errors'     => false,
            'connect_timeout' => 0.50, //////////////// 0.50
            'timeout'         => 1.00, //////////////// 1.00
            'headers' => [
              'User-Agent' => 'Test/1.0'
            ]
          ]);
    
    $requests = function ($total) {
        $uris = [
            'https://httpbin.org/get',
            'https://httpbin.org/delay/1',
            'https://httpbin.org/delay/2',
            'https://httpbin.org/status/500',
            ];
        for ($i = 0; $i < count($uris); $i++) {
            yield new Request('GET', $uris[$i]);
        }
    };
    
    $pool = new Pool($client, $requests(8), [
        'concurrency' => 10,
        'fulfilled' => function ($response, $index) {
            // this is delivered each successful response
            print_r($index."fulfilled\n");
        },
        'rejected' => function ($reason, $index) {
            // this is delivered each failed request
            print_r($index."rejected\n");
        },
    ]);
    // Initiate the transfers and create a promise
    $promise = $pool->promise();
    // Force the pool of requests to complete.
    $promise->wait();
    

    response

    0fulfilled
    3fulfilled
    1rejected
    2rejected
    

    if you want to use your code above you can also pass response status in your $promises, here is an example:

    use Psr\Http\Message\ResponseInterface;
    use GuzzleHttp\Exception\RequestException;
    ....
    $client = new Client([
        'http_errors'     => false,
        'connect_timeout' => 1.50, //////////////// 0.50
        'timeout'         => 2.00, //////////////// 1.00
        'headers' => [
          'User-Agent' => 'Test/1.0'
        ]
      ]);
    
                $promises = [
            'success' => $client->getAsync('https://httpbin.org/get')->then(
                function (ResponseInterface $res) {
                    echo $res->getStatusCode() . "\n";
                },
                function (RequestException $e) {
                    echo $e->getMessage() . "\n";
                    echo $e->getRequest()->getMethod();
                }
            )
            ,
            'success' => $client->getAsync('https://httpbin.org/delay/1')->then(
                function (ResponseInterface $res) {
                    echo $res->getStatusCode() . "\n";
                },
                function (RequestException $e) {
                    echo $e->getMessage() . "\n";
                    echo $e->getRequest()->getMethod();
                }
            ),
            'failconnecttimeout' => $client->getAsync('https://httpbin.org/delay/2')->then(
                function (ResponseInterface $res) {
                    echo $res->getStatusCode() . "\n";
                },
                function (RequestException $e) {
                    echo $e->getMessage() . "\n";
                    echo $e->getRequest()->getMethod();
                }
            ),
            'fail500' => $client->getAsync('https://httpbin.org/status/500')->then(
                function (ResponseInterface $res) {
                    echo $res->getStatusCode() . "\n";
                },
                function (RequestException $e) {
                    echo $e->getMessage() . "\n";
                    echo $e->getRequest()->getMethod();
                }
            ),
          ];
    
      $results = Promise\settle($promises)->wait();