本文目录导读:

- 服务器Cron(Linux) / 任务计划程序(Windows) ⭐ 最推荐
- PHP原生实现(适合无cron权限的虚拟主机)
- 使用第三方库(推荐生产环境)
- 消息队列方案(适合高并发)
- 使用 Workerman(适合常驻任务)
- 框架集成方案
- 最佳实践建议
在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
最佳实践建议
-
生产环境优先使用Cron - 最稳定、资源占用最低
-
任务加锁 - 防止任务重叠执行
$lockFile = '/tmp/my_task.lock'; if (file_exists($lockFile) && (time() - filemtime($lockFile)) < 300) { exit("任务正在执行中,跳过\n"); } file_put_contents($lockFile, time()); // 执行任务... unlink($lockFile); -
日志记录 - 记录执行结果和错误
-
异常处理 - 捕获所有可能的异常
-
资源清理 - 及时释放数据库连接、文件句柄等
-
监控告警 - 对长时间未执行的任务发送警报
选择合适的方案取决于你的项目规模、服务器权限和具体需求,对于大多数项目,Linux Cron + PHP脚本 是最简单可靠的方案。