PHP项目定时任务如何结合PHP实现

wen PHP项目 28

本文目录导读:

PHP项目定时任务如何结合PHP实现

  1. 服务器Cron(Linux) / 任务计划程序(Windows) ⭐ 最推荐
  2. PHP原生实现(适合无cron权限的虚拟主机)
  3. 使用第三方库(推荐生产环境)
  4. 消息队列方案(适合高并发)
  5. 使用 Workerman(适合常驻任务)
  6. 框架集成方案
  7. 最佳实践建议

在PHP项目中实现定时任务,主要有以下几种主流方案,每种适用于不同场景:

服务器Cron(Linux) / 任务计划程序(Windows) ⭐ 最推荐

Linux Cron

# 编辑crontab
crontab -e
# 每5分钟执行一次
*/5 * * * * /usr/bin/php /path/to/your/script.php
# 每天凌晨2点执行
0 2 * * * /usr/bin/php /path/to/your/script.php
# 每月1号执行
0 0 1 * * /usr/bin/php /path/to/your/script.php

脚本示例(script.php)

<?php
// 防止超时
set_time_limit(0);
// 记录开始时间
$startTime = date('Y-m-d H:i:s');
file_put_contents('/tmp/cron.log', "开始执行: {$startTime}\n", FILE_APPEND);
// 执行你的任务逻辑
try {
    // 数据库清理
    $db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $db->exec("DELETE FROM temp_data WHERE created_at < NOW() - INTERVAL 7 DAY");
    // 发送邮件通知
    // sendEmails();
    file_put_contents('/tmp/cron.log', "执行成功\n", FILE_APPEND);
} catch (Exception $e) {
    file_put_contents('/tmp/cron.log', "错误: " . $e->getMessage() . "\n", FILE_APPEND);
}

PHP原生实现(适合无cron权限的虚拟主机)

方案A:每次请求触发检查

<?php
// 在入口文件(如index.php)顶部加入
class TaskScheduler {
    private $tasks = [];
    public function addTask($name, $interval, $callback) {
        $this->tasks[$name] = [
            'interval' => $interval, // 秒
            'callback' => $callback,
            'last_run' => $this->getLastRunTime($name)
        ];
    }
    public function run() {
        foreach ($this->tasks as $name => $task) {
            $nextRun = $task['last_run'] + $task['interval'];
            if (time() >= $nextRun) {
                call_user_func($task['callback']);
                $this->updateLastRunTime($name);
            }
        }
    }
    private function getLastRunTime($name) {
        // 从文件或数据库读取上次执行时间
        $file = "/tmp/scheduler_{$name}.txt";
        return file_exists($file) ? (int)file_get_contents($file) : 0;
    }
    private function updateLastRunTime($name) {
        file_put_contents("/tmp/scheduler_{$name}.txt", time());
    }
}
// 使用
$scheduler = new TaskScheduler();
$scheduler->addTask('clean_temp', 3600, function() {
    // 每小时清理临时数据
    echo "清理临时数据...\n";
});
// 在请求处理末尾执行
$scheduler->run();

方案B:独立进程运行

<?php
// cron_daemon.php - 后台守护进程
class CronDaemon {
    private $running = true;
    private $tasks = [];
    public function addTask($name, $interval, $callback) {
        $this->tasks[$name] = [
            'interval' => $interval,
            'callback' => $callback,
            'last_run' => 0
        ];
    }
    public function start() {
        while ($this->running) {
            foreach ($this->tasks as $name => &$task) {
                if (time() - $task['last_run'] >= $task['interval']) {
                    echo "[".date('Y-m-d H:i:s')."] 执行任务: {$name}\n";
                    call_user_func($task['callback']);
                    $task['last_run'] = time();
                }
            }
            sleep(1); // 每秒检查一次
        }
    }
    public function stop() {
        $this->running = false;
    }
}
// 运行
$daemon = new CronDaemon();
$daemon->addTask('clean_temp', 3600, function() {
    // 任务内容
});
$daemon->start();

使用第三方库(推荐生产环境)

使用 cron-expression

composer require dragonmantank/cron-expression
// 使用
use Cron\CronExpression;
$cron = new CronExpression('*/5 * * * *');
if ($cron->isDue()) {
    echo "该执行任务了";
    // 执行任务
}

使用 php-cron-scheduler

composer require peppeocchi/php-cron-scheduler
// scheduler.php
require 'vendor/autoload.php';
use GO\Scheduler;
$scheduler = new Scheduler();
// 添加任务
$scheduler->php('path/to/script.php', [], 'my_task', [
    'traits' => [GO\Job\Job::TRAIT_RUN_IN_BACKGROUND]
])->everyMinute(5);
$scheduler->call(function() {
    echo "自定义任务执行中...";
})->daily();
$scheduler->run();

消息队列方案(适合高并发)

// 使用 Redis + PHP 实现延迟队列
class RedisDelayQueue {
    private $redis;
    public function __construct() {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }
    public function addTask($taskId, $delaySeconds, $callback) {
        $executeTime = time() + $delaySeconds;
        $this->redis->zAdd('delay_queue', $executeTime, json_encode([
            'id' => $taskId,
            'callback' => $callback,
            'params' => []
        ]));
    }
    public function processTasks() {
        while (true) {
            // 获取到期的任务
            $tasks = $this->redis->zRangeByScore('delay_queue', '-inf', time());
            foreach ($tasks as $task) {
                $taskData = json_decode($task, true);
                // 移除已处理的任务
                $this->redis->zRem('delay_queue', $task);
                // 执行任务
                call_user_func($taskData['callback'], $taskData['params']);
            }
            usleep(100000); // 100ms
        }
    }
}
// 使用
$queue = new RedisDelayQueue();
$queue->addTask('email_reminder', 3600, function() {
    echo "发送提醒邮件...";
});
$queue->processTasks();

使用 Workerman(适合常驻任务)

composer require workerman/workerman
// timer.php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Lib\Timer;
$worker = new Worker();
$worker->onWorkerStart = function() {
    // 每10秒执行一次
    $timerId = Timer::add(10, function() {
        echo "定时任务执行中...\n";
        // 执行任务逻辑
    });
    // 每天凌晨执行
    Timer::add(86400, function() {
        echo "每日任务执行\n";
    }, [], true); // true表示只执行一次
};
Worker::runAll();

框架集成方案

ThinkPHP

// 在 config/cron.php 配置
return [
    'cron' => [
        // 每分钟检查任务
        'check_order' => [
            'type' => 'callable',
            'callable' => function() {
                // 检查超时订单
            },
            'next_time' => time(),
            'interval' => 60,
        ],
    ],
];

Laravel

// App\Console\Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('emails:send')->daily();
    $schedule->job(new SendEmailsJob)->everyMinute();
    $schedule->call(function () {
        // 执行任务
    })->hourly();
}
// 运行调度器
php artisan schedule:run

最佳实践建议

  1. 生产环境优先使用Cron - 最稳定、资源占用最低

  2. 任务加锁 - 防止任务重叠执行

    $lockFile = '/tmp/my_task.lock';
    if (file_exists($lockFile) && (time() - filemtime($lockFile)) < 300) {
     exit("任务正在执行中,跳过\n");
    }
    file_put_contents($lockFile, time());
    // 执行任务...
    unlink($lockFile);
  3. 日志记录 - 记录执行结果和错误

  4. 异常处理 - 捕获所有可能的异常

  5. 资源清理 - 及时释放数据库连接、文件句柄等

  6. 监控告警 - 对长时间未执行的任务发送警报

选择合适的方案取决于你的项目规模、服务器权限和具体需求,对于大多数项目,Linux Cron + PHP脚本 是最简单可靠的方案。

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