使用
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
namespace App\Services\Dida;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
//use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RequestOptions;
class BaseService
{
/**
* 最大重试次数
*/
const MAX_RETRIES = 10;
protected $cache_tags;
public function __construct()
{
$this->cache_tags = config("api_token.dida.cache.tags");
}
/**
* 抓取数据接口
* @param string $uri 接口地址
* @param array $data 请求参数
* @return mixed
*/
protected function fetch($uri, array $data)
{
// 创建 Handler
$handlerStack = HandlerStack::create(new CurlHandler());
// 如果是一次发出多个请求的话,需要改为CurlMultiHandler
//$handlerStack = HandlerStack::create(new CurlMultiHandler());
// 创建重试中间件,指定决策者为 $this->retryDecider(),指定重试延迟为 $this->retryDelay()
$handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
//创建含base_uri及handler的guzzle#Client#
$client = new Client([
'base_uri' => config('api_token.dida.test.BaseUrl'),
'handler' => $handlerStack,
]);
//本次请求的post的body参数数据
$header = [
'Header' => [
'ClientID' => config('api_token.dida.test.ClientID'),
'LicenseKey' => config('api_token.dida.test.LicenseKey'),
],
];
$body = array_merge($header, $data);
// guzzle#Client# 发起post请求
// 不能用form_params 只能用json
// 设置5s超时
$req = $client->post($uri, [RequestOptions::JSON => $body, 'timeout' => 5])
->getBody()
->getContents();
return \GuzzleHttp\json_decode($req, true);
}
/**
* 返回一个匿名函数, 匿名函数若返回false 表示不重试,反之则表示继续重试
* @return \Closure
*/
private 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
*/
private function retryDelay()
{
return function ($numberOfRetries) {
return 1000 * $numberOfRetries;
};
}
}