Guzzle 6 retry middleware
Recently I switched from using Guzzle 5 to Guzzle 6 in my mediawiki-api-base PHP library. Everything went very smoothly except for there being no compatible version of the retry-subscriber that I had previously used. The subscriber has been replaced by retry middleware of which I was provided an extracted example. In this post I cover my implementation for the mediawiki-api-base library.
Guzzle 5
With Guzzle 5 you would create a retry subscriber with a filter and then attach it to the event emitter for the guzzle client. A full example can be seen below where RetrySubscriber::createStatusFilter can be seen here.
// Retry 500 and 503 responses $retry = new RetrySubscriber([ 'filter' => RetrySubscriber::createStatusFilter(), 'delay' => function ($number, $event) { return 1; }, 'max' => 3, ]); $client = new GuzzleHttp\Client(); $client->getEmitter()->attach($retry);
Guzzle 6
Guzzle 6 got rid of its event system and switched to the afore mentioned middleware system.
Middleware is added to a handler stack upon Client construction as seen below:
$handlerStack = HandlerStack::create( new CurlHandler() ); $handlerStack->push( Middleware::retry( retryDecider(), retryDelay() ) ); $client = new Client( array( 'handler' => $handlerStack ) );
The retry middleware takes two parameters, the first is the callable function that decides if a request / response should be retried and the last deciding how long to wait before retrying (similar to in Guzzle 6). Each can be seen below.
function retryDecider() { return function ( $retries, Request $request, Response $response = null, RequestException $exception = null ) { // Limit the number of retries to 5 if ( $retries >= 5 ) { return false; } // Retry connection exceptions if( $exception instanceof ConnectException ) { return true; } if( $response ) { // Retry on server errors if( $response->getStatusCode() >= 500 ) { return true; } } return false; }; }
function retryDelay() { return function( $numberOfRetries ) { return 1000 * $numberOfRetries; }; }
When implementing this for the mediawiki-api-base library I actually ended up creating a MiddlewareFactory which can be seen at on github here which is fully tested here.
This implementation includes a more complex retry decider including logging.
Enjoy!
is there a provision of setting timeout with guzzle retry middleware? I want the retry attempt to be made upon expiration of pre-defined timeout interval
Sounds like it should be possible
Thanks for the hint. The retry middleware is really a must when having to deal with an API that isn’t 100% reliable.
I’m curious, is this where OAuth token refreshes would be performed, or is that something which would have to be down further back up the chain?
I have not really worked with OAuth & Guzzle yet so can’t give you the ‘right’ answer. It definitely sounds possible though.
[…] Toujours si vous utilisez une librairie comme Guzzle, vous pouvez mettre en place un Retry Middleware. […]
I’ve been following your example & making multiple asynchronous requests with Promise/any() functions.
When retry middleware is not used & some of the api end-points are down, this is working fine. But when adding retry middleware while some end-points are down, the request gets slow. It seems that, though other end-points are up, but the failing points are blocking and retrying.
}
Request Call Code:
trait SearchesFlights
{
/* $client = new Client();*/
Should Line 20 be \GuzzleHttp\Exception\ConnectException?
I am getting following error.
Argument 4 passed to guzzelRetryClient::{closure}() must be an instance of GuzzleHttp\Psr7\RequestException, instance of GuzzleHttp\Exception\ConnectException given
Yes, namespaces are not included for any of the class names, the code expects you to “use” the classes using their full namespaces name. https://secure.php.net/manual/en/language.namespaces.importing.php
So:
use \GuzzleHttp\Exception\ConnectException;
etc…
[…] Guzzle 6 retry middleware […]
[…] Guzzle 6 retry middleware, (still #1) […]
Great article 😉 thank you sooo much!
[…] Guzzle 6 retry middleware […]
[…] Guzzle 6 retry middleware (was #5, saw increase in views) […]
[…] Guzzle 6 retry middleware (was #9) […]