首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 函數執行超時處理:register_shutdown_function 實現最大執行時間捕獲

     編輯:PHP 相關知識

PHP 函數執行超時處理:register_shutdown_function 實現最大執行時間捕獲

PHP 腳本受 max_execution_time (默認 30 秒) 限制,超時會觸發 E_ERROR 並終止執行。無法在代碼中 try/catch 捕獲此致命錯誤,但可通過 register_shutdown_function 在腳本關閉階段檢測超時並執行清理/記錄/告警邏輯。本文提供標準實現模式與進階應用。

核心機制

// 註冊關閉處理器 (可多次註冊,按順序執行)
register_shutdown_function('handleShutdown');

function handleShutdown(): void {
    // 獲取最後發生的錯誤
    $error = error_get_last();

    // 判斷是否為最大執行時間超時
    // 錯誤信息: "Maximum execution time of 30 seconds exceeded"
    if ($error !== null && strpos($error['message'], 'Maximum execution time') !== false) {
        // 超時處理邏輯
        error_log("[TIMEOUT] Script exceeded max_execution_time at {$error['file']}:{$error['line']}");

        // 可選:發送告警、寫入審計日誌、釋放鎖、回滾業務狀態
        // 注意:此處無法輸出響應給客戶端 (連接已斷開),只能做側邊效應操作
    }

    // 其他致命錯誤也會觸發
    if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        error_log("[FATAL] {$error['message']} in {$error['file']}:{$error['line']}");
    }
}

// 設置執行時間限制 (僅影響當前腳本)
set_time_limit(2); // 2秒

// 模擬長時間運行
sleep(3); // 將觸發超時

進階:類封裝與上下文感知

class TimeoutHandler {
    private static $instance;
    private $startTime;
    private $timeoutThreshold;
    private $callbacks = [];

    private function __construct(int $thresholdSeconds = 30) {
        $this->startTime = microtime(true);
        $this->timeoutThreshold = $thresholdSeconds;
        register_shutdown_function([$this, 'onShutdown']);
    }

    public static function init(int $threshold = 30): self {
        self::$instance ??= new self($threshold);
        return self::$instance;
    }

    // 註冊超時回調
    public function onTimeout(callable $callback): self {
        $this->callbacks[] = $callback;
        return $this;
    }

    public function onShutdown(): void {
        $error = error_get_last();
        $elapsed = microtime(true) - $this->startTime;

        // 判斷超時:錯誤信息匹配 OR 實際耗時超過閾值 (防止錯誤信息本地化差異)
        $isTimeout = ($error !== null && stripos($error['message'], 'maximum execution time') !== false)
                  || $elapsed >= $this->timeoutThreshold;

        if ($isTimeout) {
            foreach ($this->callbacks as $cb) {
                try {
                    $cb($elapsed, $error);
                } catch (\Throwable $e) {
                    error_log("[TimeoutHandler] Callback failed: " . $e->getMessage());
                }
            }
        }
    }

    // 手動檢查並拋出異常 (用於循環中主動檢測)
    public function check(): void {
        if (microtime(true) - $this->startTime >= $this->timeoutThreshold) {
            throw new \RuntimeException("Execution timeout threshold reached");
        }
    }
}

// 使用
TimeoutHandler::init(25) // 25秒閾值 (留 5秒緩衝給 shutdown)
    ->onTimeout(function($elapsed, $error) {
        // 記錄到監控系統
        Metrics::gauge('script.timeout', 1, ['script' => basename($_SERVER['SCRIPT_FILENAME'])]);
        // 釋放分佈式鎖
        RedisLock::release('critical_section');
        // 發送告警
        Alert::send("Script timeout after {$elapsed}s");
    });

// 長時間循環中主動檢查
for ($i = 0; $i < 10000; $i++) {
    TimeoutHandler::init()->check(); // 超時拋出異常,可被上層捕獲
    processItem($i);
}

關鍵限制與注意事項

限制說明應對
無法向客戶端輸出關閉階段連接已斷,echo 無效只能寫日誌、發隊列、調 API、更新 DB
執行時間仍受限shutdown 函數本身也受 max_execution_time 限制邏輯極簡單、快速;避免網絡調用或設置 set_time_limit(0) 在 shutdown 內
無法獲取完整堆疊error_get_last() 只返回最後一個錯誤配合 xdebug 或自定義錯誤處理器記錄堆疊
CLI vs Web 差異CLI 無 max_execution_time 默認限制 (0=無限)CLI 手動 set_time_limit() 設置
FastCGI 進程管理器Nginx + PHP-FPM 有 request_terminate_timeout設置 request_terminate_timeout > max_execution_time 讓 PHP 先觸發

替代方案對比

方案原理優點缺點適用場景
register_shutdown_function腳本結束鉤子原生、無依賴、捕獲所有致命錯誤無法輸出響應、邏輯受時限日誌記錄、資源清理、監控上報
pcntl_alarm + 信號SIGALRM 定時器可中斷阻塞系統調用、精確控制需 pcntl 擴展、僅 CLI、信號處理複雜CLI 長運行任務、守護進程
Swoole/Workerman 任務超時事件循環定時器毫秒級精度、可取消、異步非阻塞需擴展/框架、學習成本高性能異步服務、API 網關
異步隊列 + TTL消息隊列過期機制解耦、可重試、可觀測架構複雜、延遲高耗時業務異步化 (視頻轉碼、報表生成)
外部監控殺進程Supervisor/Systemd/k8s Probe徹底隔離、不污染業務代碼無法優雅關閉、數據可能不一致最後防線、不可控第三方庫調用

最佳實踐總結

  1. 設置合理閾值:Web 請求 max_execution_time = 30,CLI 任務根據業務設置 (300-3600s)。
  2. 預留緩衝:Shutdown 邏輯預留 5-10 秒,避免清理邏輯本身超時。
  3. 冪等設計:業務操作設計為冪等,超時重試不產生副作用。
  4. 監控覆蓋:超時率、平均執行時間、P99 執行時間納入核心指標儀表板。
  5. 異步化長任務:超過 30 秒的操作 (導出、轉碼、推送) 必須異步隊列處理,Web 請求只返回任務 ID。

register_shutdown_function 是 PHP 處理致命錯誤與超時的「最後一道防線」,配合合理的架構分層 (同步快速響應 + 異步重任務),構建健壯的 PHP 應用體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved