本文目录导读:

- cURL 请求
- Guzzle HTTP Client(推荐)
- 文件流获取(大文件下载重试)
- 异步请求(ReactPHP/Swoole)
- 通用重试函数
- 超时设置的注意事项
- 完整示例:带重试的API请求类
- 最佳实践建议
在PHP中进行HTTP请求时,设置超时和重试机制非常重要,以下是各种场景下的详细配置方法:
cURL 请求
基础配置
function curlRequest($url, $options = []) {
$defaults = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5, // 连接超时(秒)
CURLOPT_TIMEOUT => 30, // 总超时(秒)
CURLOPT_FOLLOWLOCATION => true, // 跟随重定向
CURLOPT_MAXREDIRS => 5, // 最大重定向次数
];
$ch = curl_init();
curl_setopt_array($ch, array_replace($defaults, $options));
$result = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("Curl error: $error");
}
curl_close($ch);
return $result;
}
重试机制实现
function requestWithRetry($url, $maxRetries = 3, $options = []) {
$retry = 0;
$delay = 1; // 初始延迟(秒)
while ($retry < $maxRetries) {
try {
$response = curlRequest($url, $options);
return $response;
} catch (Exception $e) {
$retry++;
// 达到最大重试次数
if ($retry >= $maxRetries) {
throw new Exception("Request failed after $maxRetries attempts: " . $e->getMessage());
}
// 指数退避策略
$sleepTime = $delay * pow(2, $retry - 1);
echo "Retry $retry in {$sleepTime} seconds...\n";
sleep($sleepTime);
}
}
}
// 使用示例
$url = "https://api.example.com/data";
try {
$data = requestWithRetry($url, 3, [
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 3,
]);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Guzzle HTTP Client(推荐)
安装
composer require guzzlehttp/guzzle
基础配置和重试
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\RetryMiddleware;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
// 创建客户端
$client = new Client([
'base_uri' => 'https://api.example.com',
'timeout' => 10, // 总超时
'connect_timeout' => 5, // 连接超时
'verify' => false, // 开发环境可关闭SSL验证
'http_errors' => true, // 是否抛出HTTP错误异常
]);
// 简单请求
try {
$response = $client->request('GET', '/data', [
'timeout' => 30, // 覆盖默认超时
]);
$body = $response->getBody()->getContents();
} catch (RequestException $e) {
echo "Request failed: " . $e->getMessage();
if ($e->hasResponse()) {
$statusCode = $e->getResponse()->getStatusCode();
// 根据状态码决定是否重试
if (in_array($statusCode, [408, 429, 500, 502, 503, 504])) {
// 可以在这里重试
}
}
}
Guzzle 重试中间件
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
function retryMiddleware($maxRetries = 3, $delay = 1) {
return function($handler) use ($maxRetries, $delay) {
return function($request, $options) use ($handler, $maxRetries, $delay) {
$attempt = 0;
do {
try {
$response = $handler($request, $options);
// 检查状态码
$statusCode = $response->getStatusCode();
if ($statusCode >= 500 || $statusCode == 408 || $statusCode == 429) {
if ($attempt >= $maxRetries - 1) {
return $response;
}
$attempt++;
sleep($delay * $attempt);
continue;
}
return $response;
} catch (RequestException $e) {
if ($attempt >= $maxRetries - 1) {
throw $e;
}
$attempt++;
sleep($delay * $attempt);
}
} while ($attempt < $maxRetries);
};
};
}
// 使用重试中间件
$handler = HandlerStack::create();
$retryConfig = [
'max' => 3, // 最大重试次数
'delay' => 1000, // 基础延迟(毫秒)
'retry_if' => function($retries, $response, $request) {
// 自定义重试条件
if ($retries >= 3) {
return false;
}
if ($response) {
return in_array($response->getStatusCode(), [429, 500, 503]);
}
return true; // 网络错误时重试
}
];
$handler->push(Middleware::retry($retryConfig['retry_if'], $retryConfig['delay']));
$client = new Client([
'handler' => $handler,
'timeout' => 30,
]);
文件流获取(大文件下载重试)
function downloadFileWithRetry($url, $destination, $maxRetries = 3) {
$retry = 0;
while ($retry < $maxRetries) {
try {
$ch = curl_init($url);
$fp = fopen($destination, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 3600); // 较长超时
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); // 连接超时
if (curl_exec($ch) === false) {
throw new Exception("Download failed: " . curl_error($ch));
}
curl_close($ch);
fclose($fp);
// 验证文件大小
if (filesize($destination) > 0) {
return true;
}
throw new Exception("Downloaded file is empty");
} catch (Exception $e) {
$retry++;
if ($retry >= $maxRetries) {
throw new Exception("File download failed after $maxRetries attempts");
}
// 清理部分下载的文件
if (file_exists($destination)) {
unlink($destination);
}
echo "Retry downloading... Attempt $retry\n";
sleep(2 * $retry);
}
}
}
异步请求(ReactPHP/Swoole)
Swoole 协程版本
use Swoole\Coroutine\Http\Client;
function asyncRequestWithRetry($url, $maxRetries = 3) {
$retry = 0;
while ($retry < $maxRetries) {
$client = new Client('api.example.com', 443, true);
$client->set([
'timeout' => 5,
'connect_timeout' => 3,
]);
$client->get('/path/to/resource');
if ($client->statusCode === 200) {
$result = $client->body;
$client->close();
return $result;
}
$retry++;
$client->close();
if ($retry < $maxRetries) {
co::sleep(500 * $retry); // 毫秒
}
}
return null;
}
通用重试函数
/**
* 通用重试函数
*/
function retry($callback, $maxAttempts = 3, $delay = 1000, $backoff = true) {
$attempts = 0;
while ($attempts < $maxAttempts) {
try {
return $callback();
} catch (Exception $e) {
$attempts++;
if ($attempts >= $maxAttempts) {
throw $e;
}
$waitTime = $backoff ? $delay * pow(2, $attempts - 1) : $delay;
echo "Attempt $attempts failed. Retrying in {$waitTime}ms...\n";
usleep($waitTime * 1000); // 转换为微秒
}
}
}
// 使用方式
$result = retry(function() {
return file_get_contents("https://api.example.com/data");
}, 5, 1000); // 最多重试5次,基础延迟1秒
超时设置的注意事项
合理设置超时
$config = [
// 连接超时:建立TCP连接的最大时间
'connect_timeout' => 5, // 建议 2-10 秒
// 总超时:整个请求的最大时间
'timeout' => 30, // 建议 10-60 秒
// 读超时:接收数据的最大间隔
'read_timeout' => 5, // 建议 2-10 秒
// 写超时:发送数据的最大间隔
'write_timeout' => 5, // 建议 2-10 秒
];
完整示例:带重试的API请求类
class ApiClient {
private $client;
private $maxRetries = 3;
private $delay = 1000; // 毫秒
public function __construct() {
$this->client = new Client([
'timeout' => 30,
'connect_timeout' => 5,
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'API-Client/1.0'
]
]);
}
public function request($method, $endpoint, $data = null) {
$attempts = 0;
while ($attempts < $this->maxRetries) {
try {
$response = $this->client->request($method, $endpoint, [
'json' => $data
]);
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
$attempts++;
if ($attempts >= $this->maxRetries) {
throw new Exception("API request failed: {$e->getMessage()}");
}
$waitTime = $this->delay * pow(2, $attempts - 1);
echo "Retrying request to $endpoint in {$waitTime}ms...\n";
usleep($waitTime * 1000);
} catch (Exception $e) {
// 非HTTP异常直接抛出
throw $e;
}
}
}
// 设置重试参数
public function setRetryPolicy($maxRetries, $delay = 1000) {
$this->maxRetries = $maxRetries;
$this->delay = $delay;
}
}
最佳实践建议
- 超时设置:连接超时建议 5 秒,总超时建议 30 秒,根据业务需求调整
- 重试次数:一般建议 3-5 次,过多会增加服务器压力
- 退避策略:使用指数退避(如 1s, 2s, 4s)而不是固定延迟,避免雪崩效应
- 只重试安全请求:GET、HEAD 等幂等操作可以重试;POST、PUT 等要谨慎
- 记录日志:记录重试次数和原因,便于调试
- 并发控制:高并发时应考虑限流,避免重试造成服务过载
这些方法应该能覆盖大多数 PHP HTTP 请求的超时和重试需求。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播