使用 Guzzle 中间件进行优雅的请求重试
介绍
Guzzle
是 PHP 的一款HTTP客户端包,这里介绍如何使用Guzzle提供的中间件来进行请求失败后的重试,
关于中间件的介绍可以查看文档:Handlers and Middleware
使用
先来看看Guzzle
源码中 retry
中间件的定义:
/**
* Middleware that retries requests based on the boolean result of
* invoking the provided "decider" function.
*
* If no delay function is provided, a simple implementation of exponential
* backoff will be utilized.
*
* @param callable $decider Function that accepts the number of retries,
* a request, [response], and [exception] and
* returns true if the request is to be retried.
* @param callable $delay Function that accepts the number of retries and
* returns the number of milliseconds to delay.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function retry(callable $decider, callable $delay = null)
{
return function (callable $handler) use ($decider, $delay) {
return new RetryMiddleware($decider, $handler, $delay);
};
}
retry 接收两个参数:
- $decider:重试决策,类型是
callable
,中间件根据该回调函数的返回值来决定是否进行重试。回调函数接收四个参数,分别是:当前重试次数,当前请求(GuzzleHttp\Psr7\Request),当前响应(GuzzleHttp\Psr7\Response), 当前发生的异常 (GuzzleHttp\Exception\RequestException),回调函数返回true/false,表示继续重试/停止重试 - $delay:重试延迟时间,类型也是
callable
,中间件根据该回调函数返回的值来延迟下次请求的时间,回调函数接收一个参数,是当前重试的次数,该回调函数返回下次重试的时间间隔
所以根据Retry中间件的定义,我们有如下的代码:
<?php
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
class TestGuzzleRetry
{
/**
* 最大重试次数
*/
const MAX_RETRIES = 5;
/**
* @var Client
*/
protected $client;
/**
* GuzzleRetry constructor.
*/
public function __construct()
{
// 创建 Handler
$handlerStack = HandlerStack::create(new CurlHandler());
// 创建重试中间件,指定决策者为 $this->retryDecider(),指定重试延迟为 $this->retryDelay()
$handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
// 指定 handler
$this->client = new Client(['handler' => $handlerStack]);
}
/**
* retryDecider
* 返回一个匿名函数, 匿名函数若返回false 表示不重试,反之则表示继续重试
* @return Closure
*/
protected function retryDecider()
{
return function (
$retries,
Request $request,
Response $response = null,
RequestException $exception = null
) {
// 超过最大重试次数,不再重试
if ($retries >= self::MAX_RETRIES) {
return false;
}
// 请求失败,继续重试
if ($exception instanceof ConnectException) {
return true;
}
if ($response) {
// 如果请求有响应,但是状态码大于等于500,继续重试(这里根据自己的业务而定)
if ($response->getStatusCode() >= 500) {
return true;
}
}
return false;
};
}
/**
* 返回一个匿名函数,该匿名函数返回下次重试的时间(毫秒)
* @return Closure
*/
protected function retryDelay()
{
return function ($numberOfRetries) {
return 1000 * $numberOfRetries;
};
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
挺好的。学习了 :+1:
哈哈,学到了。 现在都是靠堆代码重试,看了楼主的这个,感觉发现新大陆了..
GET 到了
GET,楼主 :+1:
感谢 我之前都是用while来进行
谢谢大佬分享
大佬 这个是使用阻塞延迟还是用队列延时的 具体延时是怎么实现的
@最后一缕阳光 阻塞的,
Retry
中间件在下次请求之前添加了delay
参数,然后Guzzle根据delay
参数,使用usleep
函数来延迟@linkjian 那时间设置太长是不是会造成连接超时
想请问一下 这里不设置 $numberOfRetries 最后 该怎么调用这个参数了?
这里提示 需要传入 callback 所以我也就不能传入 int 我该怎么才能调整这个重试延迟时间呢?