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

PHP 簡易定時任務實作:ignore_user_abort + sleep 循環與系統級 Crontab/Supervisor 最佳實踐比較

     編輯:PHP 綜合知識

PHP 本身無內建排程器,早期常見寫法是在腳本中使用 ignore_user_abort(true) + set_time_limit(0) + while(true){ sleep(); } 實作常駐進程。本文分析此寫法的缺陷,並推薦現代生產環境標準:**系統 Crontab**、**Systemd Timer**、**Supervisor 管理長運行進程**、以及 **任務佇列**。

⚠️ 不推薦:純 PHP 無限循環

<?php
// 缺陷示範:請勿在生產環境直接使用
ignore_user_abort(true);   // 瀏覽器關閉繼續執行
set_time_limit(0);         // 無執行時間限制

$interval = 300; // 5 分鐘
$url = 'https://api.example.com/heartbeat';

while (true) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
    ]);
    curl_exec($ch);
    curl_close($ch);

    sleep($interval); // 阻塞整個進程
}

嚴重缺陷

  1. 單點故障:進程掛掉 (OOM、段錯誤、網路斷線) 任務永久停止,無自動重啟。
  2. 無法平滑部署:代碼更新需手動 kill -9 重啟,易遺漏。
  3. 佔用常駐記憶體:PHP 進程長期運行易記憶體洩漏 (尤其使用未正確釋放資源的擴展)。
  4. 無日誌輪轉/監控:stdout/stderr 寫滿磁碟、無指標告警。
  5. 併發控制困難:無法限制同時執行數、無分散式鎖。

✅ 方案一:系統 Crontab (最簡單、適用 90% 場景)

# /etc/cron.d/myapp
# 每 5 分鐘執行一次
*/5 * * * * www-data /usr/bin/php /var/www/myapp/bin/cron.php heartbeat >> /var/log/myapp/cron.log 2>&1

# 每天凌晨 2 點執行重度報表
0 2 * * * www-data /usr/bin/php /var/www/myapp/bin/cron.php daily-report >> /var/log/myapp/cron.log 2>&1

優點:系統級可靠、自動重啟、日誌輪轉 (logrotate)、無需額外程式碼。
缺點:最小粒度 1 分鐘、無法防止重疊執行 (需自行加鎖)。

防止重疊執行 (Flock 鎖)

<?php
// bin/cron.php
$lockFile = fopen('/var/lock/myapp_cron.lock', 'w');
if (!flock($lockFile, LOCK_EX | LOCK_NB)) {
    exit("另一實例正在執行,跳過\n");
}

// 註冊關閉釋放鎖
register_shutdown_function(function() use ($lockFile) {
    flock($lockFile, LOCK_UN);
    fclose($lockFile);
});

// 實際任務邏輯
require __DIR__ . '/../vendor/autoload.php';
$app = new \App\Kernel();
$app->runCommand($argv[1] ?? 'default');

✅ 方案二:Systemd Timer (現代 Linux 標準,精度秒級、依賴管理)

# /etc/systemd/system/myapp-heartbeat.service
[Unit]
Description=MyApp Heartbeat Cron
After=network.target

[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php bin/cron.php heartbeat
StandardOutput=append:/var/log/myapp/heartbeat.log
StandardError=append:/var/log/myapp/heartbeat.error.log

# /etc/systemd/system/myapp-heartbeat.timer
[Unit]
Description=Run Heartbeat every 30 seconds

[Timer]
OnBootSec=1min
OnUnitActiveSec=30sec
AccuracySec=1s
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now myapp-heartbeat.timer
systemctl list-timers --all

✅ 方案三:Supervisor 管理長運行 Worker (適用佇列消費者、WebSocket 服務)

# /etc/supervisor/conf.d/myapp-worker.conf
[program:myapp-worker]
command=/usr/bin/php /var/www/myapp/bin/queue-worker.php
process_name=%(program_name)s_%(process_num)02d
numprocs=4                    # 4 個併發 worker
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/myapp/worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
environment=APP_ENV="production"
supervisorctl reread
supervisorctl update
supervisorctl status myapp-worker:*

✅ 方案四:任務佇列 (Laravel Horizon / Symfony Messenger / PHP-AMQPLib)

將「定時觸發」與「實際處理解耦」:

  1. Crontab/Systemd Timer 只負責 **推送任務到佇列** (Redis/RabbitMQ/Database)。
  2. Worker 進程 (Supervisor 管理) **消費佇列** 執行耗時邏輯。
  3. 優點:尖峰緩衝、重試機制、延遲執行、優先級、分散式擴展。
// 推送端 (Cron 執行)
$redis->lpush('queue:heartbeat', json_encode(['time' => time(), 'task' => 'heartbeat']));

// Worker 端 (Supervisor 常駐)
while (true) {
    $job = $redis->brpop('queue:heartbeat', 5); // 阻塞等待
    if ($job) {
        $data = json_decode($job[1], true);
        processHeartbeat($data);
    }
}

選型決策樹

任務頻率 ≥ 1 分鐘、邏輯輕量、無併發衝突
→ Crontab + Flock
需秒級精度、依賴其他服務啟動順序
→ Systemd Timer
長運行進程 (WebSocket、佇列消費、定時輪詢外部 API)
→ Supervisor + Worker 進程
任務耗時長、需重試、需擴展多機器
→ 任務佇列 + Supervisor Worker
PHP 專案已使用框架
→ 直接用框架排程器 Schedule::command() (Laravel) / Messenger (Symfony)

監控與告警建議

  • 健康檢查端點:Worker 定期寫入 Redis last_heartbeat,Prometheus 抓取告警超過 2 個間隔無更新。
  • 日誌聚合:Filebeat/Fluent Bit → Elasticsearch/Loki,關鍵字告警 ERROR、Exception。
  • 指標收集:執行耗時、成功/失敗計數、佇列堆積數 → Grafana 儀表板。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved