mirror of
https://github.com/guzzle/guzzle.git
synced 2025-02-25 02:22:57 +01:00
Greatly simplifying the backoff plugin
This commit is contained in:
parent
8869d8285a
commit
7f979204e8
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
use Guzzle\Http\Event\AbstractTransferStatsEvent;
|
||||
|
||||
abstract class AbstractRetryFilter implements RetryFilterInterface
|
||||
{
|
||||
/** @var RetryFilterInterface */
|
||||
protected $next;
|
||||
|
||||
/** @var array Default cURL errors to retry */
|
||||
protected static $defaultErrorCodes = array();
|
||||
|
||||
/** @var array Error codes that can be retried */
|
||||
protected $errorCodes;
|
||||
|
||||
/**
|
||||
* @param RetryFilterInterface $next The next filter in the chain
|
||||
* @param array $codes Error codes to retry
|
||||
*/
|
||||
public function __construct(RetryFilterInterface $next = null, $codes = null)
|
||||
{
|
||||
$this->next = $next;
|
||||
$this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default failure codes to retry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getDefaultFailureCodes()
|
||||
{
|
||||
return static::$defaultErrorCodes;
|
||||
}
|
||||
|
||||
public function shouldRetry($retries, AbstractTransferStatsEvent $event)
|
||||
{
|
||||
return $this->should($retries, $event)
|
||||
?: $this->next && $this->next->shouldRetry($retries, $event);
|
||||
}
|
||||
|
||||
abstract protected function should($retries, AbstractTransferStatsEvent $event);
|
||||
}
|
@ -11,23 +11,23 @@ use Guzzle\Http\Event\RequestEvents;
|
||||
*/
|
||||
class BackoffPlugin implements EventSubscriberInterface
|
||||
{
|
||||
/** @var RetryFilterInterface */
|
||||
protected $filter;
|
||||
/** @var callable */
|
||||
private $filter;
|
||||
|
||||
/** @var callable */
|
||||
protected $delayFunc;
|
||||
private $delayFunc;
|
||||
|
||||
/** @var int */
|
||||
protected $maxRetries;
|
||||
private $maxRetries;
|
||||
|
||||
/**
|
||||
* @param RetryFilterInterface $filter Filter used to determine whether or not to retry a request
|
||||
* @param callable $delayFunc Callable that accepts the number of retries and returns the amount of
|
||||
* of time in seconds to delay.
|
||||
* @param int $maxRetries Maximum number of retries
|
||||
* @param callable $filter Filter used to determine whether or not to retry a request
|
||||
* @param callable $delayFunc Callable that accepts the number of retries and returns the amount of
|
||||
* of time in seconds to delay.
|
||||
* @param int $maxRetries Maximum number of retries
|
||||
*/
|
||||
public function __construct(
|
||||
RetryFilterInterface $filter,
|
||||
callable $filter,
|
||||
callable $delayFunc,
|
||||
$maxRetries = 3
|
||||
) {
|
||||
@ -50,14 +50,16 @@ class BackoffPlugin implements EventSubscriberInterface
|
||||
array $httpCodes = null,
|
||||
array $curlCodes = null
|
||||
) {
|
||||
return new self(
|
||||
new HttpStatusFilter(
|
||||
new CurlResultFilter($curlCodes),
|
||||
$httpCodes
|
||||
),
|
||||
new ExponentialDelay(),
|
||||
$maxRetries
|
||||
);
|
||||
if (extension_loaded('curl')) {
|
||||
$filter = self::createChainFilter([
|
||||
self::createStatusFilter($httpCodes),
|
||||
self::createCurlFilter($curlCodes)
|
||||
]);
|
||||
} else {
|
||||
$filter = self::createStatusFilter($httpCodes);
|
||||
}
|
||||
|
||||
return new self($filter, array(__CLASS__, 'exponentialDelay'), $maxRetries);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
@ -70,12 +72,92 @@ class BackoffPlugin implements EventSubscriberInterface
|
||||
|
||||
public function onRequestSent(AbstractTransferStatsEvent $event)
|
||||
{
|
||||
$retries = (int) $event->getRequest()->getConfig()->get('retries');
|
||||
if ($retries < $this->maxRetries && $this->filter->shouldRetry($retries, $event)) {
|
||||
$request = $event->getRequest();
|
||||
sleep(call_user_func($this->delayFunc, $retries));
|
||||
$request->getConfig()->set('retries', ++$retries);
|
||||
$event->intercept($event->getClient()->send($request));
|
||||
$request = $event->getRequest();
|
||||
$retries = (int) $request->getConfig()->get('retries');
|
||||
|
||||
if ($retries < $this->maxRetries) {
|
||||
$filterFn = $this->filter;
|
||||
if ($filterFn($retries, $event)) {
|
||||
$delayFn = $this->delayFunc;
|
||||
sleep($delayFn($retries));
|
||||
$request->getConfig()->set('retries', ++$retries);
|
||||
$event->intercept($event->getClient()->send($request));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an exponential delay calculation
|
||||
*
|
||||
* @param int $retries Number of retries so far
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function exponentialDelay($retries)
|
||||
{
|
||||
return (int) pow(2, $retries - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a retry filter based on HTTP status codes
|
||||
*
|
||||
* @param array $failureStatuses Pass an array of status codes to override the default of [500, 503]
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function createStatusFilter(array $failureStatuses = null)
|
||||
{
|
||||
$failureStatuses = $failureStatuses ?: [500, 503];
|
||||
$failureStatuses = array_fill_keys($failureStatuses, 1);
|
||||
|
||||
return function ($retries, AbstractTransferStatsEvent $event) use ($failureStatuses) {
|
||||
if (!($response = $event->getResponse())) {
|
||||
return false;
|
||||
}
|
||||
return isset($failureStatuses[$response->getStatusCode()]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a retry filter based on cURL error codes.
|
||||
*
|
||||
* @param array $errorCodes Pass an array of curl error codes to override the default list of error codes.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function createCurlFilter($errorCodes = null)
|
||||
{
|
||||
$errorCodes = $errorCodes ?: [CURLE_COULDNT_RESOLVE_HOST,
|
||||
CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR,
|
||||
CURLE_READ_ERROR, CURLE_OPERATION_TIMEOUTED,
|
||||
CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
|
||||
CURLE_SEND_ERROR, CURLE_RECV_ERROR];
|
||||
|
||||
$errorCodes = array_fill_keys($errorCodes, 1);
|
||||
|
||||
return function ($retries, AbstractTransferStatsEvent $event) use ($errorCodes) {
|
||||
return isset($errorCodes[(int) $event->getTransferInfo('curl_result')]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chain of callables that triggers one after the other until a callable returns true.
|
||||
*
|
||||
* @param array $filters Array of callables that accept the number of retries and an after send event and return
|
||||
* true to retry the transaction or false to not retry.
|
||||
*
|
||||
* @return callable Returns a filter that can be used to determine if a transaction should be retried
|
||||
*/
|
||||
public static function createChainFilter(array $filters)
|
||||
{
|
||||
return function ($retries, AbstractTransferStatsEvent $event) use ($filters) {
|
||||
foreach ($filters as $filter) {
|
||||
if (call_user_func($filter, $retries, $event)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
use Guzzle\Http\Event\AbstractTransferStatsEvent;
|
||||
|
||||
/**
|
||||
* Strategy used to retry when certain cURL error codes are encountered.
|
||||
*/
|
||||
class CurlResultFilter extends AbstractRetryFilter
|
||||
{
|
||||
/** @var array Default cURL errors to retry */
|
||||
protected static $defaultErrorCodes = array(
|
||||
CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
|
||||
CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
|
||||
CURLE_SEND_ERROR, CURLE_RECV_ERROR
|
||||
);
|
||||
|
||||
protected function should($retries, AbstractTransferStatsEvent $event)
|
||||
{
|
||||
return isset($this->errorCodes[(int) $event->getTransferInfo('curl_result')]);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
/**
|
||||
* Implements an exponential backoff retry strategy.
|
||||
*/
|
||||
class ExponentialDelay
|
||||
{
|
||||
public function __invoke($retries)
|
||||
{
|
||||
return (int) pow(2, $retries - 1);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
use Guzzle\Http\Event\AbstractTransferStatsEvent;
|
||||
|
||||
/**
|
||||
* Strategy used to retry HTTP requests based on the response code.
|
||||
*
|
||||
* Retries 500 and 503 error by default.
|
||||
*/
|
||||
class HttpStatusFilter extends AbstractRetryFilter
|
||||
{
|
||||
/** @var array Default cURL errors to retry */
|
||||
protected static $defaultErrorCodes = [500, 503];
|
||||
|
||||
protected function should($retries, AbstractTransferStatsEvent $event)
|
||||
{
|
||||
if (!($response = $event->getResponse())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($this->errorCodes[$response->getStatusCode()]);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
use Guzzle\Http\Event\AbstractTransferStatsEvent;
|
||||
|
||||
/**
|
||||
* Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases.
|
||||
*/
|
||||
class ReasonPhraseFilter extends AbstractRetryFilter
|
||||
{
|
||||
protected function should($retries, AbstractTransferStatsEvent $event)
|
||||
{
|
||||
if (!($response = $event->getResponse())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($this->errorCodes[$response->getReasonPhrase()]);
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Guzzle\Plugin\Backoff;
|
||||
|
||||
use Guzzle\Http\Event\AbstractTransferStatsEvent;
|
||||
|
||||
/**
|
||||
* Determines if a request should be retried
|
||||
*/
|
||||
interface RetryFilterInterface
|
||||
{
|
||||
/**
|
||||
* Determines if a request should be retried
|
||||
*
|
||||
* @param int $retries Number of retries of the request
|
||||
* @param AbstractTransferStatsEvent $event Event used to determine whether or not to retry
|
||||
*
|
||||
* @return bool|int Returns false to not retry or the number of seconds to delay between retries
|
||||
*/
|
||||
public function shouldRetry($retries, AbstractTransferStatsEvent $event);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user