zengapay / guzzle_retry_middleware
Guzzle v6+ 重试中间件,处理除200、201、202以外的状态码以及连接超时
Requires
- php: ^7.1|^8.0
- guzzlehttp/guzzle: ^6.3|^7.0
Requires (Dev)
- jaschilz/php-coverage-badger: ^2.0
- nesbot/carbon: ^2.0
- phpstan/extension-installer: ^1.0
- phpstan/phpstan: ^0.12
- phpunit/phpunit: ^7.5|^8.0|^9.0
- squizlabs/php_codesniffer: ^3.5
This package is auto-updated.
Last update: 2024-09-26 17:48:55 UTC
README
这是一个 Guzzle v6+ 中间件库,当HTTP服务器响应的状态码不是 200
、201
、202
时自动重试请求。它还可以配置重试超时的请求。
如果服务器提供了一个 Retry-After 头部,此中间件将根据服务器的指示等待期延迟后续请求。
与内置的 RetryAfter
中间件不同,此中间件根据HTTP规范中的规则提供了一些默认的重试协商行为。您可以直接将其放入请求堆栈中,而无需任何额外的配置。
功能,一览无遗
- 当服务器响应状态码不是
200
、201
、202
(或任何HTTP状态码;这是可配置的)时,自动重试HTTP请求 - 根据是否发送
Retry-After
HTTP头部设置重试延迟,如果发送,则自动指数退避,如果没有发送(也可配置) - 可选地重试超时的请求(通过
connect_timeout
或timeout
选项) - 在发生重试时设置可选的回调(用于日志记录/报告)
- 在放弃之前指定最大重试次数(默认:10)
- 近100%的测试覆盖率,良好的内联文档,并符合PSR-12规范
安装
通过Composer
$ composer require zengapay/guzzle_retry_middleware
用法
基本上
use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleRetry\GuzzleRetryMiddleware; $stack = HandlerStack::create(); $stack->push(GuzzleRetryMiddleware::factory()); $client = new Client(['handler' => $stack]); // Make requests galore...
这是默认配置。如果一个HTTP服务器响应状态码不是 200
、201
、202
,这个中间件将拦截响应并重试,最多重试10次,然后放弃并执行Guzzle默认的行为(默认情况下,抛出 BadResponseException
)。
如果服务器提供了一个 RetryAfter
头部,这个中间件将等待指定的时间再次尝试请求。如果没有,那么它将退避,每次请求之间等待的时间更长,直到在第10次尝试后放弃。
选项
以下选项可用
以下选项将在下面详细讨论。
配置选项
选项可以在三个地方设置之一
// Per request, in the same array as other Guzzle options $response = $client->get('/some-url', [ 'max_retry_attempts' => 5, 'on_retry_callback' => $notifier ]); // When you instantiate Guzzle, in the same array as other Guzzle options $client = new \GuzzleHttp\Client([ // Standard Guzzle options 'base_url' => 'http://example.org', 'connect_timeout' => 10.0, // Retry options 'max_retry_attempts' => 5, 'on_retry_callback' => $notifier ]); // When you instantiate the Retry middleware $stack = \GuzzleHttp\Stack::create(); $stack->push(GuzzleRetryMiddleware::factory([ 'max_retry_attempts' => 5, 'on_retry_callback' => $notifier ]));
如果您在两个或更多地方指定选项,则配置将按以下方式合并
- 请求选项优先于Guzzle构造函数选项
- Guzzle构造函数选项优先于中间件构造函数选项。
设置最大重试尝试次数
此值应是一个等于或大于0的整数。将此值设置为0或负数将有效地禁用此中间件。
将此值设置为0很有用,当您想默认重试尝试,但想为特定的请求禁用重试时
// Set the default retry attempts to 5 $client = new \GuzzleHttp\Client(['max_retry_attempts' => 5]); // Do not retry this request $client->get('/some/url', ['max_retry_attempts' => 0]);
设置不重试的状态码
默认情况下,此中间件将在服务器不响应HTTP状态码 200
、201
或 202
时重试请求。但是,您可以配置此
$response = $client->get('/some-path', [ 'retry_on_status_other_than' => [200, 201, 202] ]);
如果响应包含 RetryAfter
头部,但其状态码不在列表中,则不会处理。
设置默认重试延迟
如果响应包含有效的 RetryAfter
头部,此中间件将延迟下一次重试尝试,延迟时间为服务器在该头部中指定的延迟时间。
如果响应包含一个无效的 RetryAfter 或未提供 RetryAfter
报头,则此中间件将使用默认的退避算法:multipler * number-of-attempts
包含 RetryAfter
报头的响应
Client Server
------ ------
GET /resource ->
<- 429 Response with `Retry-After: 120`
wait 120s
GET /resource ->
<- 200 OK
没有 RetryAfter
,请求数量乘以倍增器(默认:1.5
)
Client Server
------ ------
GET /resource ->
<- 429 Response (no Retry-After header)
wait 1.5 x 1s
GET /resource ->
<- 429 Response (no Retry-After header)
wait 1.5 x 2s
GET /resource ->
<- 429 Response (no Retry-After header)
wait 1.5 x 3s
GET /resource ->
<- 429 Response (no Retry-After header)
wait 1.5 x 4s
GET /resource ->
<- 200 OK
您可以为自定义默认倍增器设置
$response = $client->get('/some-path', [ 'default_retry_multiplier' => 2.5 ]);
您还可以为 default_retry_multiplier
指定一个可调用的函数来设置默认延迟超时,以设置自定义算法
// Custom callback to determine default timeout. Note: $response may be NULL if a connect timeout occurred. $response = $client->get('/some-path', [ 'default_retry_multiplier' => function($numRequests, ?ResponseInterface $response): float { return (float) rand(1, 5); } ]);
重试超时请求
您可以配置此中间件以重试超时请求。只需将 retry_on_timeout
选项设置为 true
# Retry this request if it times out: $response = $client->get('/some-path', [ 'retry_on_timeout' => true, // Set the retry middleware to retry when the connection or response times out 'connect_timeout' => 20, // This is a built-in Guzzle option 'timeout' => 50 // This is also a built-in Guzzle option ]); # You can also set these as defaults for every request: $guzzle = new \GuzzleHttp\Client(['retry_on_timeout' => true, 'connect_timeout' => 20]); $response = $guzzle->get('https://example.org');
重试回调
您可以提供一个回调方法,该方法将在每次重试请求时被调用。这对于日志记录、报告或其他您能想到的事情很有用。
如果指定了回调,它将在中间件调用 usleep()
延迟函数之前被调用。
request
和 options
参数通过引用发送,以防您想在回调中修改它们。
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; /** * Listen for retry events * * @param int $attemptNumber How many attempts have been tried for this particular request * @param float $delay How long the client will wait before retrying the request * @param RequestInterface $request Request * @param array $options Guzzle request options * @param ResponseInterface|null $response Response (or NULL if response not sent; e.g. connect timeout) */ $listener = function($attemptNumber, $delay, &$request, &$options, $response) { echo sprintf( "Retrying request to %s. Server responded with %s. Will wait %s seconds. This is attempt #%s", $request->getUri()->getPath(), $response->getStatusCode(), number_format($delay, 2), $attemptNumber ); } $client = new \GuzzleHttp\Client([ 'on_retry_callback' => $listener ]); $response = $client->get('/some/path');
按请求启用或禁用
假设您已设置如下默认重试选项
$stack = \GuzzleHttp\Stack::create(); $stack->push(GuzzleRetryMiddleware::factory(['max_retry_attempts' => 5])); $client = new \GuzzleHttp\Client(['handler' => $stack]);
您可以通过在请求选项中设置 retry_enabled
参数来禁用单个请求的重试
// Retry will NOT be attempted for this request.. $client->get('http://example.org', ['retry_enabled' => false]); // Retry WILL be attempted for this request... $client->get('http://example.org');
向 HTTP 响应添加自定义重试报头
有时,为了调试目的,了解在获取响应时请求重试了多少次很有用。为此,此库可以添加自定义报头到响应;只需将 expose_retry_header
选项设置为 TRUE
。
注意:这修改了客户端的 HTTP 响应。如果您不想修改从服务器获取的响应,您也可以使用 回调 来获取请求计数。
示例
# Retry this request if it times out: $response = $client->get('/some-path', [ 'expose_retry_header' => true // This adds the 'X-Retry-Counter' if a request was retried ]); # If a request was retried, the response will include the 'X-Retry-Counter' $numRetries = (int) $response->getHeaderLine('X-Retry-Counter');
您也可以指定一个自定义报头键
# Retry this request if it times out: $response = $client->get('/some-path', [ 'expose_retry_header' => true, 'retry_header' => 'X-Retry-Count' ]); # If a request was retried, the response will include the 'X-Retry-Counter' $numRetries = (int) $response->getHeaderLine('X-Retry-Count');
修改客户端期望的服务器响应的报头名称从 Retry-After
您可以更改客户端期望服务器响应的报头。默认情况下,客户端查找 Retry-After
报头,但在某些边缘情况下,服务器可能会选择响应不同的报头。
# Change the name of the expected retry after header to something else: $response = $client->get('/some-path', [ 'retry_after_header' => 'X-Custom-Retry-After-Seconds' ]); # Otherwise, the default `Retry-After` header will be used. $response = $client->get('/some-path');
为 Retry-After
报头设置自定义日期格式
您可以更改客户端库期望的服务器期望的日期格式。默认情况下,此库期望 RFC 2822 报头,如 HTTP 规范 中定义。在特定的边缘情况下,服务器可能实现其他日期格式。此库允许这种可能性。
# Change the expected date format of the `Retry-After` header $response = $client->get('/some-path', [ 'retry_after_date_format' => 'Y-m-d H:i:s' ]); # Otherwise, the default date format for the `Retry-After` header will be used. # (ex. 'Wed, 24 Nov 2020 07:28:00 GMT') $response = $client->get('/some-path');
注意:请注意不要使用 Unix 纪元(u
)格式。客户端将此值解释为整数,并随后超时很长时间。
。
设置最大允许的超时值
如果您希望客户端不接受超过一定值的超时值,请设置 max_allowable_timeout_secs
选项。无论是否使用默认退避算法或从服务器通过 Retry-After
报头返回,当超时达到指定长度时,此选项将返回一个静态数字。
默认情况下,此值为 null
,表示没有限制。
# Set the maximum allowable timeout # If the calculated value exceeds 120 seconds, then just return 120 seconds $response = $client->get('/some-path', [ 'max_allowable_timeout_secs' => 120 ]);
变更日志
有关最近更改的更多信息,请参阅 变更日志
测试
$ composer test
注意:由于此库测试超时,一些测试需要 2-3 秒才能运行。
贡献
有关详细信息,请参阅 贡献
鸣谢
许可证
MIT许可证(MIT)。请参阅许可证文件获取更多信息。