本文目录导读:

在 PHP 中处理超时涉及多个层面,根据你的应用场景(Web 请求、脚本执行、外部API调用等),有以下几种处理方式:
Web 请求超时
PHP 脚本执行超时
// 设置执行时间限制(秒)
set_time_limit(30); // 30秒
ini_set('max_execution_time', 30);
// 如果设置为0,表示无限执行时间
set_time_limit(0);
客户端请求超时
// 检查是否超时
if (connection_status() != CONNECTION_NORMAL) {
// 客户端断开连接,处理超时
exit('Connection lost');
}
// 设置响应超时
ini_set('max_execution_time', 30);
ini_set('max_input_time', 60);
外部 API/HTTP 请求超时
cURL 超时
function fetchUrl($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5, // 连接超时(秒)
CURLOPT_TIMEOUT => 10, // 整个请求超时(秒)
CURLOPT_TIMEOUT_MS => 10000, // 毫秒级别超时
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
if ($errno === CURLE_OPERATION_TIMEOUTED) {
curl_close($ch);
throw new Exception('请求超时');
}
curl_close($ch);
return $response;
}
file_get_contents 超时
$ctx = stream_context_create([
'http' => [
'timeout' => 5, // 秒
'ignore_errors' => true,
],
'ssl' => [
'verify_peer' => false,
]
]);
$response = @file_get_contents('https://api.example.com', false, $ctx);
if ($response === false) {
$error = error_get_last();
if (strpos($error['message'], 'timed out') !== false) {
echo "请求超时";
}
}
数据库查询超时
PDO 设置超时
try {
// 设置连接超时(MySQL)
$pdo = new PDO(
'mysql:host=localhost;dbname=test;charset=utf8',
'user',
'password',
[
PDO::ATTR_TIMEOUT => 5, // 连接超时
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]
);
// 设置查询超时(MySQL 8+)
$pdo->exec("SET SESSION wait_timeout = 10");
$pdo->exec("SET SESSION max_execution_time = 5000"); // 毫秒
} catch (PDOException $e) {
echo "数据库连接超时: " . $e->getMessage();
}
MySQLi 设置超时
$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5);
$mysqli->options(MYSQLI_OPT_READ_TIMEOUT, 10);
$mysqli->options(MYSQLI_OPT_WRITE_TIMEOUT, 10);
if (!$mysqli->real_connect($host, $user, $pass, $db)) {
if ($mysqli->connect_errno === 2002) { // 连接超时错误码
echo "数据库连接超时";
}
}
文件上传/下载超时
// 配置文件上传超时
ini_set('max_execution_time', 300);
ini_set('max_input_time', 600);
ini_set('memory_limit', '256M');
ini_set('post_max_size', '10M');
ini_set('upload_max_filesize', '10M');
自定义超时处理机制
使用 pcntl 信号处理(Unix/Linux)
declare(ticks = 1);
function timeoutHandler($signo) {
switch ($signo) {
case SIGALRM:
echo "执行超时,进行清理操作\n";
exit(1);
}
}
pcntl_signal(SIGALRM, 'timeoutHandler');
pcntl_alarm(10); // 10秒后触发SIGALRM
// 你的长任务
while (true) {
// 执行操作
}
// 取消定时器
pcntl_alarm(0);
使用 register_shutdown_function
register_shutdown_function(function() {
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
// 处理致命错误(可能包含超时)
echo "脚本因超时或错误终止: " . $error['message'];
// 进行清理操作
}
});
set_time_limit(5);
// 长时间执行的代码
while (true) {
// 业务逻辑
}
异步/并发请求超时
// 使用 Swoole 协程(高性能场景)
Swoole\Coroutine\run(function () {
$timeout = 3;
$result = Swoole\Coroutine\go(function () {
// 需要执行的请求
$client = new Swoole\Coroutine\Http\Client('api.example.com', 443, true);
$client->set(['timeout' => 2]);
$client->get('/endpoint');
return $client->body;
});
if (Swoole\Coroutine\channel()->pop($timeout) === null) {
echo "请求超时";
}
});
综合超时处理类
class TimeoutHandler {
private $startTime;
private $timeout;
public function __construct($timeout) {
$this->startTime = microtime(true);
$this->timeout = $timeout;
}
public function checkTimeout() {
$elapsed = microtime(true) - $this->startTime;
if ($elapsed >= $this->timeout) {
throw new Exception("操作超时,已耗时 " . round($elapsed, 2) . " 秒");
}
}
public function getRemainingTime() {
return $this->timeout - (microtime(true) - $this->startTime);
}
public function setConnectionTimeout($seconds) {
$this->timeout = $seconds;
}
}
// 使用示例
try {
$handler = new TimeoutHandler(10);
// 需要监控的长时间操作
while (true) {
$handler->checkTimeout(); // 检查是否超时
// 执行部分工作
// ...
// 在关键节点检查
if ($handler->getRemainingTime() < 2) {
// 剩余时间不足,提前退出
break;
}
}
} catch (Exception $e) {
echo "超时处理: " . $e->getMessage();
}
最佳实践建议
配置调整(php.ini)
; 生产环境推荐配置 max_execution_time = 60 max_input_time = 120 memory_limit = 256M session.gc_maxlifetime = 1440
队列任务超时(如 Laravel)
// Laravel 队列任务设置超时
class ProcessJob {
public $timeout = 60; // 秒
public $tries = 3; // 重试次数
public function handle() {
// 长时间处理逻辑
}
}
使用缓存机制避免重复超时
// 防止重复执行长时间任务
$lockKey = 'long_task_lock';
if (!apcu_fetch($lockKey)) {
apcu_store($lockKey, true, 300); // 5分钟锁
try {
// 执行长时间任务
set_time_limit(240);
// ...
} finally {
apcu_delete($lockKey);
}
}
总结要点
- 区分场景:Web请求、CLI脚本、外部API调用需要不同的超时策略
- 合理设置:超时时间要根据业务需求设置,不宜过短或过长
- 优雅处理:超时后要能正确清理资源,返回友好的错误信息
- 监控记录:记录超时日志,分析是否需要对超时时间进行调整
- 重试机制:对于不稳定的外部依赖,考虑添加适当的重试策略
选择合适的方法时要考虑你的使用场景:是处理用户请求、后台任务,还是与外部系统交互,对于高并发生产环境,建议结合队列、消息系统或异步框架来管理复杂的超时场景。