本文目录导读:

在 PHP 中定制调度(Scheduling)通常有以下几种场景和方法,我来详细说明:
使用 Cron 调度任务
基础 Cron 配置
// crontab 配置示例 * * * * * php /path/to/your/script.php
使用第三方库(推荐)
composer require dragonmantank/cron-expression
<?php
use Cron\CronExpression;
// 解析 Cron 表达式
$cron = CronExpression::factory('*/5 * * * *');
// 检查是否该执行
if ($cron->isDue()) {
// 执行任务
echo "任务执行中...";
}
// 获取下次运行时间
$nextRun = $cron->getNextRunDate();
echo "下次运行时间: " . $nextRun->format('Y-m-d H:i:s');
使用 Laravel 任务调度
定义调度任务
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// 基础调度
$schedule->command('emails:send')
->daily()
->at('08:00')
->timezone('Asia/Shanghai');
// 复杂调度
$schedule->call(function () {
// 执行任务代码
DB::table('users')->delete();
})->everyMinute();
// 带条件的调度
$schedule->command('backup:run')
->weekly()
->when(function () {
return config('app.backup_enabled');
});
// 任务重叠保护
$schedule->command('report:generate')
->everyFiveMinutes()
->withoutOverlapping(30);
}
自定义调度器实现
简单调度类
<?php
class TaskScheduler {
private $tasks = [];
public function addTask($name, callable $callback, $schedule) {
$this->tasks[] = [
'name' => $name,
'callback' => $callback,
'schedule' => $schedule
];
}
public function run() {
foreach ($this->tasks as $task) {
if ($this->shouldRun($task['schedule'])) {
try {
$start = microtime(true);
call_user_func($task['callback']);
$this->logExecution($task['name'], microtime(true) - $start);
} catch (Exception $e) {
$this->logError($task['name'], $e->getMessage());
}
}
}
}
private function shouldRun($schedule) {
// 检查时间是否匹配
$now = new DateTime();
if (isset($schedule['time'])) {
$expectedTime = new DateTime($schedule['time']);
if ($now->format('H:i') !== $expectedTime->format('H:i')) {
return false;
}
}
if (isset($schedule['interval'])) {
$interval = $schedule['interval'];
$lastRun = $this->getLastRun(); // 从数据库获取上次运行时间
if ($lastRun && ($now->getTimestamp() - $lastRun) < $interval) {
return false;
}
}
return true;
}
private function logExecution($taskName, $duration) {
// 记录日志
error_log("[{$taskName}] 执行完成,耗时: {$duration}秒");
}
}
// 使用示例
$scheduler = new TaskScheduler();
$scheduler->addTask('清理缓存', function() {
cache()->flush();
}, ['interval' => 3600]);
$scheduler->addTask('发送邮件', function() {
// 发送邮件逻辑
}, ['time' => '09:00']);
使用 RabbitMQ / Redis 队列调度
Redis 队列调度
<?php
class RedisScheduler {
private $redis;
private $scheduleKey;
public function __construct(Redis $redis, $scheduleKey) {
$this->redis = $redis;
$this->scheduleKey = $scheduleKey;
}
public function schedule($delay, $callback) {
$task = [
'callback' => serialize($callback),
'execute_at' => time() + $delay
];
$this->redis->zAdd($this->scheduleKey, $task['execute_at'], json_encode($task));
}
public function process() {
while (true) {
$tasks = $this->redis->zRangeByScore($this->scheduleKey, 0, time());
foreach ($tasks as $taskData) {
$task = json_decode($taskData, true);
// 执行任务
$callback = unserialize($task['callback']);
$callback();
// 移除已执行的任务
$this->redis->zRem($this->scheduleKey, $taskData);
}
sleep(1);
}
}
}
完整的企业级调度系统
<?php
interface ScheduleInterface {
public function getNextRun(\DateTime $currentTime): \DateTime;
public function isDue(\DateTime $time): bool;
}
class CronSchedule implements ScheduleInterface {
private $expression;
private $parser;
public function __construct($expression) {
$this->parser = new \Cron\CronExpression($expression);
$this->expression = $expression;
}
public function getNextRun(\DateTime $currentTime): \DateTime {
return $this->parser->getNextRunDate($currentTime);
}
public function isDue(\DateTime $time): bool {
return $this->parser->isDue($time);
}
}
class TaskManager {
private $tasks = [];
private $storage;
private $logger;
public function __construct($storage, $logger) {
$this->storage = $storage;
$this->logger = $logger;
}
public function register($name, $task, ScheduleInterface $schedule, array $options = []) {
$this->tasks[$name] = [
'task' => $task,
'schedule' => $schedule,
'options' => $options
];
}
public function run() {
$currentTime = new DateTime();
foreach ($this->tasks as $name => $config) {
try {
if (!$this->shouldRun($name, $config, $currentTime)) {
continue;
}
$this->executeTask($name, $config);
} catch (Exception $e) {
$this->logger->error("任务 {$name} 执行失败: {$e->getMessage()}");
}
}
}
private function shouldRun($name, $config, $currentTime): bool {
$schedule = $config['schedule'];
$lastRun = $this->storage->getLastRun($name);
if (!$lastRun) {
return true;
}
// 检查是否满足调度条件
$nextRun = $schedule->getNextRun($lastRun);
return $currentTime >= $nextRun;
}
private function executeTask($name, $config) {
$startTime = microtime(true);
// 添加任务锁防止重复执行
if (isset($config['options']['lock']) && $config['options']['lock']) {
$lockKey = "task_lock_{$name}";
if (!$this->acquireLock($lockKey)) {
return;
}
}
try {
$result = call_user_func($config['task']);
// 记录执行状态
$this->storage->setLastRun($name, new DateTime());
$this->storage->setLastResult($name, $result);
$duration = microtime(true) - $startTime;
$this->logger->info(
"任务 {$name} 执行成功",
[
'duration' => $duration,
'result' => $result
]
);
} finally {
// 释放锁
if (isset($config['options']['lock']) && $config['options']['lock']) {
$this->releaseLock($lockKey);
}
}
}
private function acquireLock($key): bool {
// 使用 Redis 实现分布式锁
return $this->redis->set($key, 1, ['NX', 'EX' => 60]);
}
private function releaseLock($key) {
$this->redis->del($key);
}
}
使用环境配置
// config/scheduler.php
return [
'default_timezone' => 'Asia/Shanghai',
'tasks' => [
[
'name' => 'send_notifications',
'schedule' => '*/5 * * * *',
'callback' => [NotificationService::class, 'sendPending'],
'params' => ['limit' => 100]
],
[
'name' => 'cleanup_logs',
'schedule' => '0 3 * * *',
'callback' => [LogService::class, 'cleanup'],
'params' => ['days_old' => 30]
]
],
'locking' => [
'enabled' => true,
'driver' => 'redis'
],
'monitoring' => [
'enabled' => true,
'metrics' => ['execution_time', 'memory_usage'],
'alerts' => ['email' => 'admin@example.com']
]
];
最佳实践建议
// 1. 任务状态记录
class TaskStatusMiddleware {
public function __invoke($task, callable $next) {
$startTime = microtime(true);
$taskId = uniqid('task_');
try {
$result = $next();
$this->recordSuccess($taskId, $result, microtime(true) - $startTime);
return $result;
} catch (\Exception $e) {
$this->recordFailure($taskId, $e, microtime(true) - $startTime);
throw $e;
}
}
}
// 2. 动态任务注册
class DynamicScheduler {
public function loadTasksFromConfig() {
$configs = config('scheduler.tasks');
foreach ($configs as $config) {
if ($this->shouldRegister($config)) {
$this->registerTask(
$config['name'],
$config['callback'],
$config['schedule']
);
}
}
}
}
选择合适的调度方案取决于:
- 项目复杂度
- 部署环境(是否有 Cron)
- 是否需要实时调度
- 可用基础设施(Redis、DB 等)
- 监控和调试需求
推荐从简单的 Cron 或 Laravel 调度开始,随着需求增长再升级到复杂的队列调度系统。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播