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

Linux 系統下 PHP 定時任務最佳實踐:Crontab + Shell 腳本

     編輯:PHP 相關知識

PHP 本身無內建排程器,常見錯誤做法是用 ignore_user_abort() + sleep() 長連線模擬,極易造成記憶體洩漏、進程殭屍。生產環境應採用 Linux Crontab + Shell 包裝 PHP CLI 架構,穩定、可監控、易維護。

架構圖解

+----------------+     +----------------+     +------------------+
|  Crontab 排程  | --> |  run_task.sh   | --> |  php task.php    |
|  (每分鐘檢查)  |     |  (環境/日誌)   |     |  (業務邏輯)      |
+----------------+     +----------------+     +------------------+

步驟 1:撰寫 PHP CLI 入口腳本 (task.php)

<?php
// task.php - 純 CLI 模式,無 HTTP 頭輸出
define('APP_PATH', __DIR__ . '/../');
require_once APP_PATH . 'bootstrap.php'; // 載入框架/資料庫設定

// 1. 參數解析 (支援多任務)
$action = $argv[1] ?? 'default';
$params = array_slice($argv, 2);

// 2. 記憶體/時間限制
ini_set('memory_limit', '256M');
set_time_limit(0); // CLI 預設 0 (無限制)

// 3. 執行對應任務
try {
    switch ($action) {
        case 'sync_orders':
            (new OrderService())->syncFromERP();
            break;
        case 'daily_report':
            (new ReportService())->generateDaily();
            break;
        case 'cleanup_logs':
            (new LogService())->cleanup(30); // 保留 30 天
            break;
        default:
            echo "Usage: php task.php [action] [params...]\n";
            exit(1);
    }
    echo "[OK] $action completed at " . date('Y-m-d H:i:s') . "\n";
    exit(0);
} catch (Throwable $e) {
    error_log("[FAIL] $action: " . $e->getMessage());
    exit(1);
}

步驟 2:建立 Shell 包裝腳本 (run_task.sh)

#!/bin/bash
# run_task.sh - 統一環境、日誌、鎖機制

# 專案根目錄
PROJECT_ROOT="/var/www/myapp"
PHP_BIN="/usr/bin/php"          # 或 /usr/local/php/bin/php
LOG_DIR="$PROJECT_ROOT/storage/logs/cron"
LOCK_DIR="/tmp/cron_locks"

mkdir -p "$LOG_DIR" "$LOCK_DIR"

# 參數檢查
if [ $# -lt 1 ]; then
    echo "Usage: $0  [params...]"
    exit 1
fi

ACTION=$1
shift
PARAMS=$@

# 防止重複執行 (flock)
LOCK_FILE="$LOCK_DIR/${ACTION}.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
    echo "[$(date)] $ACTION 仍在執行中,跳過本次"
    exit 0
fi

# 執行並記錄日誌 (stdout/stderr 合併)
LOG_FILE="$LOG_DIR/${ACTION}_$(date +%Y%m%d).log"
echo "=== [$ACTION] START $(date) ===" >> "$LOG_FILE"
cd "$PROJECT_ROOT"
$PHP_BIN task.php "$ACTION" $PARAMS >> "$LOG_FILE" 2>&1
EXIT_CODE=$?
echo "=== [$ACTION] END $(date) EXIT=$EXIT_CODE ===" >> "$LOG_FILE"

exit $EXIT_CODE

步驟 3:設定 Crontab 排程

# 編輯當前用戶的 crontab
crontab -e

# 範例排程 (每行: 分 時 日 月 週 指令)
# 每天 02:30 同步訂單
30 2 * * * /var/www/myapp/run_task.sh sync_orders >> /dev/null 2>&1

# 每小時第 5 分產生報表
5 * * * * /var/www/myapp/run_task.sh daily_report >> /dev/null 2>&1

# 每週一 03:00 清理舊日誌
0 3 * * 1 /var/www/myapp/run_task.sh cleanup_logs >> /dev/null 2>&1

# 每 2 分鐘檢查隊列 (高頻任務)
*/2 * * * * /var/www/myapp/run_task.sh process_queue >> /dev/null 2>&1

關鍵最佳實踐

實踐說明
使用 PHP CLI 模式避開 Web Server (Nginx/Apache) 超時、記憶體限制、Session 鎖
Shell 腳本統一入口集中處理環境變數、日誌輪轉、鎖機制、錯誤碼傳遞
flock 防重複執行上次任務未結束時自動跳過,防止並發衝突
日誌分檔 + 輪轉按日期/任務分檔,配合 logrotate 自動清理
非零退出碼告警結合 Zabbix/Promtail 監控退出碼,失敗即時通知
鎖目錄用 /tmp重開機自動清理,避免殘留鎖導致任務永不執行

常見坑與解法

Q: Crontab 不執行 / 執行無效果?
1. 指令需絕對路徑 (PHP、Shell、專案目錄)
2. 環境變數不同:在 Shell 腳本開頭 source ~/.bashrc 或直接 export PATH=...
3. 權限問題:確保 Shell 腳本 chmod +x,且執行用戶對專案目錄有讀寫權
Q: 任務卡住不釋放?
PHP 內設定 set_time_limit(300) (5分鐘) 防止無限循環;Shell 層用 timeout 300 強制殺掉。
Q: 如何傳遞複雜參數 (JSON、陣列)?
Shell 寫入暫存檔 echo '$json' > /tmp/params.json,PHP 讀取 json_decode(file_get_contents(...), true)。

進階:系統級 Timer (systemd timer) 替代 Crontab

現代 Linux (CentOS 7+, Ubuntu 16.04+) 建議改用 systemd timer,優點:精準到秒、支援隨機延遲、依賴管理、日誌整合 journald。範例單元檔:

# /etc/systemd/system/php-task@.service
[Unit]
Description=PHP Task %i
[Service]
Type=oneshot
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php task.php %i
User=www-data

# /etc/systemd/system/php-task@.timer
[Unit]
Description=Run PHP Task %i periodically
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target

# 啟用
systemctl daemon-reload
systemctl enable --now php-task@sync_orders.timer
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved