How can I get Guzzle 6 to retry a request upon a 503 error in Laravel

13,542

Solution 1

Guzzle by default throws exceptions when a non 2** response is returned. In your case you're seeing a 503 response. Exceptions can be thought of as errors that the application can recover from. The way this works is with try catch blocks.

try {
    // The code that can throw an exception will go here
    throw new \Exception('A generic error');
    // code from here down won't be executed, because the exception was thrown.
} catch (\Exception $e) {
    // Handle the exception in the best manner possible.
}

You wrap the code that could throw an exception in the try portion of the block. Then you add your error handling code in the catch portion of the block. You can read the above link for more information on how php handles exceptions.

For your case, lets move the Guzzle call to it's own method in your controller:

public function performLookUp($retryOnError = false)
{
    try {
        $client = new \GuzzleHttp\Client();
        $request = $client->request('GET', 'https://httpbin.org/status/503'); 
        return $request->send();
    } catch (\GuzzleHttp\Exception\BadResponseException $e) {
        if ($retryOnError) {
            return $this->performLookUp();
        }
        abort(503);
   }
}

Now in your controller you can execute, $this->performLookUp(true);.

Solution 2

Just to add some information to clarify a few points that Logan made.

Guzzle "can" throw exceptions on a Response other then 2**/3**. It all depends upon how the GuzzleHttp\HandlerStack is created.

$stack = GuzzleHttp\HandlerStack::create();
$client = new Client(['handler'=> $stack]);

$client = new Client();
// These two methods of generating a client are functionally the same.


$stack = New GuzzleHttp\HandlerStack(new GuzzleHttp\Handler\CurlHandler());
$client = new Client(['handler'=> $stack]);
// This client will not throw exceptions, or perform any of the functions mentioned below.

The create method adds default handlers to the HandlerStack. When the HandlerStack is resolved, the handlers will execute in the following order:

  1. Sending request:
    1. http_errors - No op when sending a request. The response status code is checked in the response processing when returning a response promise up the stack.
    2. allow_redirects - No op when sending a request. Following redirects occurs when a response promise is being returned up the stack.
    3. cookies - Adds cookies to requests.
    4. prepare_body - The body of an HTTP request will be prepared (e.g., add default headers like Content-Length, Content-Type, etc.).
    5. send request with handler
  2. Processing response:
    1. prepare_body - no op on response processing.
    2. cookies - extracts response cookies into the cookie jar.
    3. allow_redirects - Follows redirects. 4.http_errors - throws exceptions when the response status code >= 300.

When provided no $handler argument, GuzzleHttp\HandlerStack::create() will choose the most appropriate handler based on the extensions available on your system. As indicated within the Handler Documentation

By manually creating your GuzzleHttp\HandlerStack, you can add middleware to the application. Given the context of your original question "how do i repeat the request" I believe you are most interested in the Retry Middleware that is provided within Guzzle 6.1. This is a middleware that retries requests based on the result of the provided decision function.

Documentation has yet to catch up with this class.

Share:
13,542
ZeroGodForce
Author by

ZeroGodForce

Updated on June 17, 2022

Comments

  • ZeroGodForce
    ZeroGodForce almost 2 years

    I've written some code in Laravel 5.2 to retrieve results from an unrelible API source. However, it needs to be able to automatically retry the request on failed attempts, as the API call results in a 503 about a third of the time.

    I'm use Guzzle to do this, and I think I know where to put the code that will intercept the 503 responses before they are processed; but I'm not sure what to actually write there.

    The guzzle documentation doesn't offer much as far as retries go, and all of the examples I've come across of Guzzle 6 only show how to retrieve results (which I can already do), but not how to get it to repeat the request if needed.

    I'm by no means asking anyone to do the work for me - but I think I'm approaching the limits of my understanding on this. If anybody can point me in the right direction, it'd be much appreciated :)

    EDIT:

    I will try and revise. Please consider the following code. In it, I want to send a GET request which should normally yield a JSON response.

    DataController.php

    $client = new \GuzzleHttp\Client();
    $request = $client->request('GET', 'https://httpbin.org/status/503'); // URI is for testing purposes
    

    When the response from this request is a 503, I can intercept it here:

    Handler.php

    public function render($request, Exception $e)
    {
      if ($e->getCode() == 503)
      {
        // Code that would tell Guzzle to retry the request 5 times with a 10s delay before failing completely
      }
    
      return parent::render($request, $e);
    }
    

    I don't know that that is the best place to put it, but the real problem is I don't know is what to write inside the if ($e->getCode() == 503)