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

PHP 定時任務實現方案大全:從 Crontab 到純 PHP 多進程 Timer

     編輯:PHP 相關知識

定時任務三大主流方案對比

方案原理優點缺點適用場景
Crontab + ShellLinux 系統級調度穩健、獨立於 PHP、支援分鐘級依賴 Linux、分散式部署難管理常規定時備份、報表、清理
ignore_user_abort + set_time_limitHTTP 請求觸發常駐腳本無需系統權限、代碼控制邏輯只能單實例、易超時、佔用 FPM 進程簡單輪詢、輕量任務
純 PHP CLI + PCNTL (本文重點)常駐進程 + 信號驅動毫秒級精度、進程隔離、支援持久化任務需 CLI 環境、PCNTL 擴展、Linux 為主高併發隊列消費、即時推送、遊戲邏輯循環

純 PHP Timer 核心原理

  1. 靜態三維數組存儲任務:[$timestamp => [[$interval, $callback, $args, $persist], ...]]
  2. PCNTL 信號:每秒發送 SIGALRM 給當前進程
  3. 信號處理器捕獲信號 → 遍歷任務隊列 → 到期則 call_user_func_array 執行
  4. 自循環:執行完重新 pcntl_alarm(1) 設置下一秒鬧鐘
  5. 主循環:while(true) { sleep(1); pcntl_signal_dispatch(); } 保持進程存活並分發信號

Timer 類完整實現

class Timer
{
    public static $tasks = [];
    public static $tick = 1; // 秒

    public static function run(int $tick = null): void
    {
        if ($tick) self::$tick = $tick;
        self::installHandler();
        pcntl_alarm(1);
    }

    public static function installHandler(): void
    {
        pcntl_signal(SIGALRM, [self::class, 'signalHandler']);
    }

    public static function signalHandler(): void
    {
        self::executeDueTasks();
        pcntl_alarm(self::$tick); // 重新裝填鬧鐘
    }

    public static function executeDueTasks(): void
    {
        if (empty(self::$tasks)) return;

        $now = time();
        foreach (self::$tasks as $timestamp => &$jobs) {
            foreach ($jobs as $index => $job) {
                if ($now >= $timestamp) {
                    // 執行回調
                    call_user_func_array($job['callback'], $job['args'] ?? []);

                    // 非持久化任務執行完移除
                    if (!$job['persist']) {
                        unset($jobs[$index]);
                    } else {
                        // 持久化:重新計算下次執行時間
                        $nextTime = $now + $job['interval'];
                        self::$tasks[$nextTime][] = $job;
                        unset($jobs[$index]);
                    }
                }
            }
            if (empty($jobs)) unset(self::$tasks[$timestamp]);
        }
    }

    public static function add(int $interval, callable $callback, array $args = [], bool $persist = false): void
    {
        $time = time() + $interval;
        self::$tasks[$time][] = [
            'callback' => $callback,
            'args' => $args,
            'interval' => $interval,
            'persist' => $persist
        ];
    }

    public static function clear(): void
    {
        self::$tasks = [];
    }
}

使用示例

require 'Timer.php';

class Heartbeat
{
    public function beat(array $meta = []): void
    {
        echo "[" . date('H:i:s') . "] Heartbeat: " . json_encode($meta) . PHP_EOL;
    }
}

Timer::clear();

// 每 1 秒執行一次,持久化
Timer::add(1, [new Heartbeat(), 'beat'], ['type' => 'sys'], true);

// 3 秒後執行一次,非持久化
Timer::add(3, [new Heartbeat(), 'beat'], ['type' => 'once'], false);

echo "Timer started at " . date('H:i:s') . PHP_EOL;
Timer::run();

// 主進程保持存活並分發信號
while (true) {
    sleep(1);
    pcntl_signal_dispatch();
}

生產環境部署關鍵點

  • 進程守護:用 Supervisor、Systemd 或 Docker `restart: always` 確保進程掛掉自動拉起
  • 多進程擴展:啟動多個 Worker 進程,配合 Redis/RabbitMQ 分發任務
  • 優雅關閉:捕獲 SIGTERM 信號,完成當前任務後退出
  • 日誌與監控:記錄執行耗時、異常堆棧,接入 Prometheus/Grafana
  • 精度限制:PHP pcntl_alarm 僅支援秒級,毫秒級需用 Swoole/Workerman/ReactPHP

現代替代方案推薦

框架/庫特點
Swoole / Hyperf協程、毫秒定時器、高併發、內建 Server
Workerman純 PHP、多進程、定時器、長連接
Laravel Scheduler藝術家風格、一行代碼定義、底層仍靠 Crontab
Symfony Messenger + Messenger Consume消息總線、異步處理、可配合 Crontab 觸發

總結

理解 PCNTL + Signal + 靜態任務隊列 的純 PHP Timer 原理,能讓你在無法安裝擴展的受限環境裡實現可靠定時任務。但生產環境強烈建議升級到 Swoole/Hyperf/Workerman,獲得協程調度、毫秒精度、更豐富的生態支持。選對工具,讓定時任務成為系統穩健運行的「心跳」而非「定時炸彈」。

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