调用接口时,如果没有返回数据,我们就增加下一次调用的间隔时间。我们可以使用一个简单的退避策略,例如每次没有数据返回时,等待时间翻倍,直到达到一个最大等待时间。同时,如果有数据返回,我们就重置等待时间。
方案一:基础重试机制
<?php
function callApiWithRetry($url, $maxRetries = 5, $initialDelay = 1000) {
$retries = 0;
$delay = $initialDelay;
while ($retries < $maxRetries) {
// 调用接口
$response = file_get_contents($url);
$data = json_decode($response, true);
// 检查是否有数据
if (!empty($data)) {
return $data;
}
// 无数据,增加等待时间
$retries++;
echo "第 {$retries} 次重试,等待 {$delay} 毫秒\n";
// 等待指定时间(毫秒)
usleep($delay * 1000);
// 增加延迟时间(指数退避)
$delay *= 2;
}
throw new Exception("达到最大重试次数,未获取到数据");
}
// 使用示例
try {
$result = callApiWithRetry('https://api.example.com/data', 5, 1000);
print_r($result);
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
方案二:使用cURL的进阶版本
<?php
class ApiCaller {
private $maxRetries;
private $initialDelay;
private $maxDelay;
public function __construct($maxRetries = 5, $initialDelay = 1000, $maxDelay = 30000) {
$this->maxRetries = $maxRetries;
$this->initialDelay = $initialDelay;
$this->maxDelay = $maxDelay;
}
public function callWithRetry($url, $options = []) {
$retries = 0;
$delay = $this->initialDelay;
while ($retries < $this->maxRetries) {
$response = $this->callApi($url, $options);
// 检查响应是否有效
if ($this->isValidResponse($response)) {
return $response;
}
$retries++;
echo "无数据返回,第 {$retries} 次重试,等待 " . ($delay/1000) . " 秒\n";
// 等待
usleep($delay * 1000);
// 指数退避,但不超过最大延迟
$delay = min($delay * 2, $this->maxDelay);
}
throw new Exception("接口调用失败,达到最大重试次数");
}
private function callApi($url, $options) {
$ch = curl_init();
$defaultOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
]
];
curl_setopt_array($ch, array_merge($defaultOptions, $options));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
curl_close($ch);
throw new Exception('cURL错误: ' . curl_error($ch));
}
curl_close($ch);
return [
'http_code' => $httpCode,
'data' => $response
];
}
private function isValidResponse($response) {
// 检查HTTP状态码
if ($response['http_code'] !== 200) {
return false;
}
// 解析JSON数据
$data = json_decode($response['data'], true);
// 检查数据是否为空
if (empty($data)) {
return false;
}
// 可以根据具体API返回结构自定义检查逻辑
// 例如:检查特定字段是否存在数据
if (isset($data['data']) && empty($data['data'])) {
return false;
}
return true;
}
}
// 使用示例
$apiCaller = new ApiCaller(5, 1000, 30000); // 最多重试5次,初始延迟1秒,最大延迟30秒
try {
$result = $apiCaller->callWithRetry('https://api.example.com/data');
print_r($result);
} catch (Exception $e) {
echo "API调用失败: " . $e->getMessage();
}
?>
方案三:带有随机抖动的重试策略
<?php
class SmartApiCaller {
private $maxRetries;
private $baseDelay;
private $maxDelay;
public function __construct($maxRetries = 5, $baseDelay = 1000, $maxDelay = 60000) {
$this->maxRetries = $maxRetries;
$this->baseDelay = $baseDelay;
$this->maxDelay = $maxDelay;
}
public function callWithExponentialBackoff($url, $callback = null) {
$retries = 0;
while ($retries <= $this->maxRetries) {
try {
$response = $this->makeRequest($url);
// 使用回调函数检查响应
if ($callback) {
$isValid = call_user_func($callback, $response);
} else {
$isValid = $this->defaultValidation($response);
}
if ($isValid) {
return $response;
}
} catch (Exception $e) {
echo "请求异常: " . $e->getMessage() . "\n";
}
if ($retries < $this->maxRetries) {
$delay = $this->calculateDelay($retries);
echo "等待 {$delay} 毫秒后重试...\n";
usleep($delay * 1000);
}
$retries++;
}
throw new Exception("所有重试尝试均失败");
}
private function makeRequest($url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return [
'http_code' => $httpCode,
'body' => $response,
'json' => json_decode($response, true)
];
}
private function defaultValidation($response) {
return $response['http_code'] === 200 &&
!empty($response['body']) &&
!empty($response['json']);
}
private function calculateDelay($retryCount) {
// 指数退避 + 随机抖动
$exponentialDelay = $this->baseDelay * pow(2, $retryCount);
$jitter = rand(0, 500); // 添加0-500ms的随机抖动
return min($exponentialDelay + $jitter, $this->maxDelay);
}
}
// 使用示例
$caller = new SmartApiCaller();
// 自定义验证回调
$validationCallback = function($response) {
// 检查特定的业务逻辑
if ($response['http_code'] !== 200) {
return false;
}
$data = $response['json'];
return isset($data['success']) && $data['success'] && !empty($data['result']);
};
try {
$result = $caller->callWithExponentialBackoff(
'https://api.example.com/data',
$validationCallback
);
print_r($result);
} catch (Exception $e) {
echo "最终失败: " . $e->getMessage();
}
?>
方案四:使用Guzzle HTTP客户端
如果你使用Composer,可以安装Guzzle来简化HTTP请求:
通过Composer可快速集成Guzzle到项目中:
composer require guzzlehttp/guzzle
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class GuzzleApiCaller {
private $client;
private $maxRetries;
public function __construct($maxRetries = 5) {
$this->client = new Client(['timeout' => 30]);
$this->maxRetries = $maxRetries;
}
public function callWithRetry($url) {
$retries = 0;
$delay = 1000; // 1秒
while ($retries < $this->maxRetries) {
try {
$response = $this->client->get($url);
$data = json_decode($response->getBody(), true);
if (!empty($data)) {
return $data;
}
echo "无数据,准备重试...\n";
} catch (RequestException $e) {
echo "请求失败: " . $e->getMessage() . "\n";
}
$retries++;
if ($retries < $this->maxRetries) {
echo "等待 " . ($delay/1000) . " 秒后重试\n";
usleep($delay * 1000);
$delay *= 2; // 指数退避
}
}
throw new Exception("达到最大重试次数");
}
}
// 使用示例
$caller = new GuzzleApiCaller();
try {
$result = $caller->callWithRetry('https://api.example.com/data');
print_r($result);
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
说明
指数退避: 每次重试的等待时间翻倍
最大重试次数: 防止无限循环
随机抖动: 避免多个客户端同时重试
灵活验证: 支持自定义响应验证逻辑
异常处理: 完善的错误处理机制