借鉴:
使用 Guzzle 中间件进行优雅的请求重试 | Laravel China 社区
基础配置(我图方便,直接放在了App\Common里面):
<?php
/**
* GuzzleRetry,针对Guzzle请求重试
*/
namespace App\common;
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 GuzzleRetry
{
/**
* 最大重试次数
*/
const MAX_RETRIES = 5;
/**
* 每次请求间隔时间:秒
*/
const RETRY_DELAY = 2;
/**
* @var Client
*/
public $client;
/**
* GuzzleRetry constructor.
*/
public function __construct($url)
{
// 创建 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,'base_uri'=>$url]);
}
/**
* 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 * self::RETRY_DELAY;
};
}
}
具体实现:
private function request(string $method = 'POST', string $uri = '/login', array $body = [])
{
$headers = [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $this->token,
'Content-Type' => 'application/json',
'Accept-Language' => 'en-US'
];
$client = new GuzzleRetry($this->url);
try {
$response = $client->client->request($method, $uri, ['json' => $body, 'headers' => $headers, 'verify' => false]);
} catch (\Exception $e) {
Log::error('Request ' . $uri . ' failed.Error :' . $e->getMessage());
return ["status" => false, "error" => 'Request api failed.'];
}
$code = $response->getStatusCode();
if ($code > 299 || $code < 200) {
Log::error('Request ' . $uri . 'failed.HttpCode error.');
return ["status" => false, "error" => 'Request api failed.'];
}
try {
$content = $response->getBody()->getContents();
$content = json_decode($content, true);
$content['status'] = (strtolower($content['status']) == 'success') ? true : false;
} catch (\Exception $e) {
Log::error('Request ' . $uri . 'failed.Error:Return data format error.');
return ["status" => false, "error" => 'Request api failed.'];
}
return $content;
}
需要初始化的是:
url, 比如说是 http://127.0.0.1/api/v1
token: XXXXX
这样就会自动重试
当然, 万一我不想那么优雅, 那就直接在发送请求那里重试吧
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $request,
CURLOPT_RETURNTRANSFER => true,//获取的信息以文件流的形式返回,不直接输出
CURLOPT_HEADER => false,//不返回header部分
CURLOPT_ENCODING => '',//Accept-Encoding编码,支持"identity"/"deflate"/"gzip",空支持所有编码
CURLOPT_MAXREDIRS => 10,//指定最多的HTTP重定向的数量
CURLOPT_TIMEOUT => 0,//连接后等待时间,0不等待。如下载mp3
CURLOPT_FOLLOWLOCATION => true,//跟踪爬取重定向页面
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,//设置curl使用的HTTP协议
CURLOPT_SSL_VERIFYPEER => false,//禁止 cURL 验证对等证书
CURLOPT_SSL_VERIFYHOST => false//是否检测服务器的域名与证书上的是否一致
]);
$response = curl_exec($ch);
//如果出错了, 3次重试, 每次间隔2S
$tried = 3;
while (
isset($response['errors']) &&
$tried > 0
){
sleep(2);
$response = curl_exec($ch);
$response = json_decode($response, true);
$tried--;
}
curl_close($ch);
return $response;