本文目录导读:

在PHP项目中处理请求超时异常,可以从以下几个层面进行:
PHP脚本执行时间限制
设置执行时间
// 设置无限制执行时间(不安全) set_time_limit(0); // 设置具体秒数 set_time_limit(300); // 5分钟 // 也可以在php.ini中设置 ; max_execution_time = 300
捕获超时异常
try {
// 某些耗时操作
$result = someTimeConsumingOperation();
} catch (Exception $e) {
if (strpos($e->getMessage(), 'Maximum execution time') !== false) {
// 处理超时异常
error_log('Script execution timed out: ' . $e->getMessage());
http_response_code(503);
echo json_encode(['error' => '请求超时,请稍后重试']);
} else {
throw $e; // 重新抛出其他异常
}
}
数据库查询超时
MySQL查询超时
// 设置MySQL查询超时(5秒)
$pdo = new PDO($dsn, $user, $pass);
$pdo->exec("SET SESSION max_execution_time = 5000");
// 或使用PDO Statement超时
$stmt = $pdo->prepare("SELECT * FROM large_table");
$stmt->execute();
Redis操作超时
$redis = new Redis();
try {
$redis->connect('127.0.0.1', 6379, 2.5); // 2.5秒连接超时
$redis->setOption(Redis::OPT_READ_TIMEOUT, 5); // 5秒读取超时
$result = $redis->get('some_key');
} catch (RedisException $e) {
error_log('Redis timeout: ' . $e->getMessage());
// 降级处理
}
cURL请求超时
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10, // 连接超时10秒
CURLOPT_TIMEOUT => 30, // 总超时30秒
CURLOPT_TIMEOUT_MS => 30000, // 毫秒单位
]);
$response = curl_exec($ch);
if (curl_errno($ch) == CURLE_OPERATION_TIMEDOUT) {
error_log('cURL request timed out');
echo "请求超时,请稍后重试";
}
curl_close($ch);
HTTP请求超时(使用Guzzle)
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\TransferException;
$client = new Client([
'timeout' => 10, // 总超时
'connect_timeout' => 5, // 连接超时
'read_timeout' => 30, // 读取超时
]);
try {
$response = $client->get('https://api.example.com/data');
} catch (ConnectException $e) {
error_log('Connection timeout: ' . $e->getMessage());
} catch (TransferException $e) {
error_log('Transfer timeout: ' . $e->getMessage());
}
进程管理方案
使用pcntl实现超时控制
declare(ticks = 1);
// 设置超时信号
function timeout_handler($signo) {
throw new \RuntimeException('Process timeout', 408);
}
pcntl_signal(SIGALRM, 'timeout_handler');
pcntl_alarm(30); // 30秒超时
try {
// 耗时操作
longRunningProcess();
pcntl_alarm(0); // 取消闹钟
} catch (\RuntimeException $e) {
if ($e->getCode() == 408) {
error_log('Process timed out');
// 降级处理
}
}
异步处理和队列方案
使用消息队列处理长任务
// 1. 提交任务到队列
$jobId = $queue->push(new LongRunningJob($data));
// 2. 返回立即响应
echo json_encode([
'status' => 'accepted',
'job_id' => $jobId,
'message' => '任务已提交,稍后查看结果'
]);
// 3. 后台Worker处理
class LongRunningJob
{
public function handle()
{
try {
set_time_limit(600); // 允许10分钟
// 处理任务...
} catch (\Exception $e) {
Log::error('Job timeout: ' . $e->getMessage());
// 重试或记录失败
}
}
}
全局超时中间件(框架示例)
Laravel/Symfony中间件
namespace App\Http\Middleware;
use Closure;
use Symfony\Component\HttpKernel\Exception\HttpException;
class RequestTimeout
{
public function handle($request, Closure $next, $timeout = 30)
{
// 设置最大执行时间
set_time_limit($timeout);
try {
$response = $next($request);
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'Maximum execution time') !== false) {
throw new HttpException(504, 'Request timeout');
}
throw $e;
}
return $response;
}
}
最佳实践建议
分层超时设置
class TimeoutHandler
{
const DEFAULT_TIMEOUT = 30;
const DB_TIMEOUT = 5;
const API_TIMEOUT = 10;
const FILE_TIMEOUT = 60;
public static function handle(callable $callback, $timeout = self::DEFAULT_TIMEOUT)
{
$startTime = microtime(true);
try {
$result = $callback();
$elapsed = microtime(true) - $startTime;
if ($elapsed > $timeout) {
throw new TimeoutException("Operation exceeded {$timeout}s");
}
return $result;
} catch (TimeoutException $e) {
// 记录慢查询
Log::warning('Slow operation detected', [
'timeout' => $timeout,
'elapsed' => $elapsed ?? 0
]);
// 返回降级数据
return self::getFallbackData();
}
}
private static function getFallbackData()
{
return ['error' => '请求超时', 'status' => 'timeout'];
}
}
// 使用
$result = TimeoutHandler::handle(function() {
return $api->getData();
}, TimeoutHandler::API_TIMEOUT);
超时监控和告警
// 记录超时事件用于监控
$timeoutMetrics = [
'timestamp' => time(),
'endpoint' => $_SERVER['REQUEST_URI'],
'method' => $_SERVER['REQUEST_METHOD'],
'timeout_seconds' => $timeout,
];
// 发送到监控系统
Metrics::increment('request.timeout', $timeoutMetrics);
总结建议
- 区分超时类型:连接超时、响应超时、执行超时等
- 设置合理超时:根据业务场景调整(API调用5-10s,文件处理30-60s)
- 优雅降级:超时时返回友好的错误信息或缓存数据
- 异步处理:长任务使用消息队列异步执行
- 监控告警:记录超时事件并设置告警
- 前端配合:前端设置合理的AJAX超时并显示加载状态
- 避免无限等待:所有外部调用必须设置超时
选择哪种方案取决于你的具体业务需求和系统架构,如果问题依然存在,建议排查慢查询、网络延迟、资源竞争等根本原因。