PHP 怎么超时处理

wen PHP项目 2

本文目录导读:

PHP 怎么超时处理

  1. Web 请求超时
  2. 外部 API/HTTP 请求超时
  3. 数据库查询超时
  4. 文件上传/下载超时
  5. 自定义超时处理机制
  6. 异步/并发请求超时
  7. 综合超时处理类
  8. 最佳实践建议
  9. 总结要点

在 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);
    }
}

总结要点

  1. 区分场景:Web请求、CLI脚本、外部API调用需要不同的超时策略
  2. 合理设置:超时时间要根据业务需求设置,不宜过短或过长
  3. 优雅处理:超时后要能正确清理资源,返回友好的错误信息
  4. 监控记录:记录超时日志,分析是否需要对超时时间进行调整
  5. 重试机制:对于不稳定的外部依赖,考虑添加适当的重试策略

选择合适的方法时要考虑你的使用场景:是处理用户请求、后台任务,还是与外部系统交互,对于高并发生产环境,建议结合队列、消息系统或异步框架来管理复杂的超时场景。

上一篇PHP 怎么长期运行流程

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!