本文目录导读:

在PHP项目中处理超时请求,通常需要从应用层、服务器层和客户端层三个维度进行控制,以下是具体的实现方案:
脚本执行超时控制
使用 set_time_limit()
在脚本开头设置最大执行时间:
// 设置脚本最大执行时间为30秒 set_time_limit(30); // 或者动态调整 $timeout = 60; set_time_limit($timeout);
使用 register_shutdown_function() 捕获超时
register_shutdown_function(function() {
$error = error_get_last();
if ($error && $error['type'] === E_ERROR) {
// 处理超时错误
http_response_code(504);
echo json_encode(['error' => '请求超时']);
}
});
set_time_limit(5);
// 模拟耗时操作
sleep(10); // 这里会触发超时
HTTP请求超时控制
cURL 超时设置
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'http://example.com/api',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10, // 连接超时(秒)
CURLOPT_TIMEOUT => 30, // 总超时(秒)
CURLOPT_TIMEOUT_MS => 30000, // 毫秒级超时 (PHP 5.2.3+)
]);
$response = curl_exec($ch);
if (curl_errno($ch) == CURLE_OPERATION_TIMEDOUT) {
// 处理超时
echo "请求超时";
}
curl_close($ch);
file_get_contents() 超时控制
$opts = [
'http' => [
'method' => 'GET',
'timeout' => 30, // 秒
]
];
$context = stream_context_create($opts);
$result = @file_get_contents('http://example.com', false, $context);
Guzzle HTTP 客户端超时
use GuzzleHttp\Client;
$client = new Client([
'timeout' => 30.0, // 响应超时
'connect_timeout' => 10.0, // 连接超时
'read_timeout' => 30.0, // 读取超时
'write_timeout' => 30.0, // 写入超时
]);
try {
$response = $client->get('http://example.com');
} catch (\GuzzleHttp\Exception\ConnectException $e) {
// 连接超时
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 请求超时
}
数据库查询超时
MySQL 查询超时
// PDO
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$pdo->exec("SET SESSION MAX_EXECUTION_TIME = 5000"); // 毫秒
// mysqli
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->query("SET SESSION MAX_EXECUTION_TIME = 5000");
Redis 操作超时
$redis = new Redis();
$redis->connect('127.0.0.1', 6379, 2.5); // 连接超时2.5秒
$redis->setOption(Redis::OPT_READ_TIMEOUT, 5); // 读取超时5秒
try {
$value = $redis->get('key');
} catch (RedisException $e) {
// 处理超时
}
服务器层面配置
Nginx 配置
# 在 server 或 location 块中配置
location ~ \.php$ {
fastcgi_read_timeout 300; # FastCGI 超时时间(秒)
fastcgi_connect_timeout 60; # 连接超时
fastcgi_send_timeout 300; # 发送超时
proxy_read_timeout 300; # 代理读取超时
}
Apache 配置
# httpd.conf 或 .htaccess
<IfModule mod_fcgid.c>
FcgidIOTimeout 300
FcgidBusyTimeout 300
FcgidConnectTimeout 60
</IfModule>
# PHP-FPM 配置
<IfModule mod_proxy_fcgi.c>
ProxyTimeout 300
</IfModule>
PHP-FPM 配置
; php-fpm.conf 或 pool.d/www.conf request_terminate_timeout = 300 ; 请求终止超时 request_slowlog_timeout = 30 ; 慢请求记录阈值 slowlog = /var/log/php-fpm/slow.log max_execution_time = 300 max_input_time = 300
超时处理最佳实践
队列处理长任务
// 使用消息队列处理耗时任务
class AsyncTaskProcessor {
public function handleLongTask($data) {
// 1. 将任务推入队列
$queue->push('process_task', $data);
// 2. 立即返回处理中状态
return ['status' => 'processing', 'task_id' => $taskId];
}
// 3. 后台 worker 处理实际逻辑
public function processTask($data) {
set_time_limit(0); // 无超时限制
// 执行耗时操作...
}
}
使用进程管理工具
// 使用 pcntl_fork 处理超时
$pid = pcntl_fork();
if ($pid == -1) {
die('Could not fork');
} elseif ($pid) {
// 父进程:设置超时计时器
$timeout = 30;
sleep($timeout);
// 如果子进程还在运行,发送终止信号
posix_kill($pid, SIGTERM);
pcntl_wait($status);
} else {
// 子进程:执行实际任务
set_time_limit(0);
// 执行耗时操作...
exit(0);
}
优雅的超时处理
class TimeoutHandler {
private $startTime;
private $timeout;
public function __construct($timeout = 30) {
$this->startTime = time();
$this->timeout = $timeout;
}
public function checkTimeout() {
if (time() - $this->startTime > $this->timeout) {
throw new TimeoutException('操作超时');
}
}
public function executeWithTimeout(callable $callback) {
while (true) {
$this->checkTimeout();
$result = $callback();
if ($result !== null) {
return $result;
}
usleep(100000); // 100ms
}
}
}
// 使用示例
$handler = new TimeoutHandler(10);
try {
$result = $handler->executeWithTimeout(function() {
// 执行需要超时控制的操作
return checkStatus();
});
} catch (TimeoutException $e) {
// 处理超时
echo $e->getMessage();
}
监控与告警
记录超时日志
class TimeoutLogger {
public static function log($context) {
$logData = [
'timestamp' => date('Y-m-d H:i:s'),
'url' => $_SERVER['REQUEST_URI'] ?? 'unknown',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown',
'timeout' => $context['timeout'],
'execution_time' => $context['execution_time']
];
error_log(json_encode($logData) . "\n", 3, '/var/log/php_timeout.log');
}
}
使用性能监控工具
- Xdebug:跟踪执行时间和内存使用
- Blackfire.io:性能分析
- New Relic:应用性能监控
常见问题及解决方案
| 问题 | 解决方案 |
|---|---|
| 文件上传超时 | 使用分片上传或异步处理 |
| 大量数据处理超时 | 使用分页或游标查询 |
| 第三方API响应慢 | 设置合理的超时时间,使用缓存 |
| 复杂计算超时 | 使用生成器或流式处理 |
推荐策略:对于新项目,建议使用队列系统(如RabbitMQ、Redis Queue)处理耗时操作,前端配合轮询或WebSocket获取结果,这比单纯增加超时时间更健壮。