本文目录导读:

在PHP项目中调用外部接口时,超时处理至关重要,能防止接口卡死导致整个请求阻塞,以下是几种常见方式的超时处理方案:
cURL 方式(最常用)
基础超时设置
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.example.com/endpoint");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 总超时时间(秒)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // 连接超时时间(秒)
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
$errno = curl_errno($ch);
// 超时错误处理
if ($errno == CURLE_OPERATION_TIMEDOUT) {
echo "请求超时,请稍后重试";
// 记录日志
error_log("API 超时: " . $error);
}
}
curl_close($ch);
更精细的超时控制
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.example.com/endpoint");
// 毫秒级超时设置
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10000); // 10秒总超时(毫秒)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 5000); // 5秒连接超时(毫秒)
// 设置重试次数
$maxRetries = 3;
$attempt = 0;
$response = false;
while ($attempt < $maxRetries && !$response) {
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch) != 0) {
$attempt++;
if ($attempt < $maxRetries) {
usleep(500000); // 等待0.5秒后重试
}
} else {
break;
}
}
file_get_contents 方式
$options = [
'http' => [
'method' => 'GET',
'timeout' => 10, // 超时时间(秒)
'ignore_errors' => true
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
];
$context = stream_context_create($options);
try {
$response = file_get_contents("https://api.example.com/endpoint", false, $context);
if ($response === false) {
throw new Exception("请求失败");
}
} catch (Exception $e) {
echo "接口调用超时或失败: " . $e->getMessage();
// 记录错误日志
}
Guzzle HTTP 客户端(推荐)
安装
composer require guzzlehttp/guzzle
使用示例
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ClientException;
$client = new Client([
'timeout' => 10, // 总超时
'connect_timeout' => 5, // 连接超时
'read_timeout' => 10, // 读取超时
'retry' => 3 // 重试次数
]);
try {
$response = $client->request('GET', 'https://api.example.com/endpoint', [
'headers' => [
'Authorization' => 'Bearer your_token'
]
]);
$body = $response->getBody();
$data = json_decode($body, true);
} catch (ConnectException $e) {
// 连接超时
echo "连接超时: " . $e->getMessage();
} catch (RequestException $e) {
// 请求超时或其他错误
if ($e->hasResponse()) {
$statusCode = $e->getResponse()->getStatusCode();
echo "请求失败,状态码: " . $statusCode;
} else {
echo "请求异常: " . $e->getMessage();
}
}
异步超时处理(Swoole/ReactPHP)
Swoole 协程方式
use Swoole\Coroutine\Http\Client;
use function Swoole\Coroutine\run;
run(function () {
$cli = new Client('api.example.com', 443, true);
$cli->set(['timeout' => 10]);
$cli->get('/endpoint');
if ($cli->statusCode === -1) {
echo "请求超时";
} else {
echo $cli->body;
}
$cli->close();
});
完整封装示例
class ApiClient
{
private $timeout = 10;
private $connectTimeout = 5;
private $maxRetries = 3;
public function request($method, $url, $data = [])
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => $this->connectTimeout,
CURLOPT_CUSTOMREQUEST => strtoupper($method),
]);
if (!empty($data)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
}
$attempt = 0;
$response = null;
while ($attempt < $this->maxRetries) {
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
$errno = curl_errno($ch);
// 成功,跳出循环
if ($httpCode >= 200 && $httpCode < 300) {
break;
}
// 连接超时或请求超时,重试
if ($errno == CURLE_OPERATION_TIMEDOUT ||
$errno == CURLE_COULDNT_CONNECT) {
$attempt++;
if ($attempt < $this->maxRetries) {
$waitTime = pow(2, $attempt); // 指数退避
sleep($waitTime);
continue;
}
}
break;
}
curl_close($ch);
if ($response === false) {
throw new \Exception("API 请求失败: " . ($error ?? "未知错误"));
}
return json_decode($response, true);
}
}
// 使用示例
try {
$client = new ApiClient();
$result = $client->request('GET', 'https://api.example.com/data');
} catch (\Exception $e) {
echo "请求失败: " . $e->getMessage();
// 执行降级逻辑
}
最佳实践建议
超时时间设置
- 连接超时:3-5秒
- 读取超时:10-30秒(根据业务需求)
- 总超时:不超过30秒
错误处理策略
// 降级处理
function handleApiFailure($error)
{
// 1. 使用缓存数据
if ($cacheData = getFromCache('api_data')) {
return $cacheData;
}
// 2. 返回默认值
return ['status' => 'error', 'message' => '服务暂时不可用'];
// 3. 记录告警
sendAlert("API 服务异常: " . $error);
}
日志记录
function logApiCall($url, $response, $duration, $error = null)
{
$logData = [
'url' => $url,
'duration' => $duration,
'response_code' => $response['code'] ?? null,
'error' => $error,
'timestamp' => date('Y-m-d H:i:s')
];
// 写入日志文件
file_put_contents(
'/var/log/api_calls.log',
json_encode($logData) . PHP_EOL,
FILE_APPEND
);
}
监控告警
- 设置超时率告警阈值(如超过5%)
- 监控响应时间趋势
- 设置失败重试机制
- 实现熔断器模式
选择合适的超时方案取决于项目的具体情况,对于重要的生产环境,推荐使用 Guzzle 并配合完善的错误处理和重试机制。