spiral-packages/scheduler
包提供了一个简单的 API,用于通过计划运行 cron 作业任务。它可以轻松地与基于 Spiral 的项目集成。
要安装该包,您可以使用以下命令:
composer require spiral-packages/scheduler
安装完该包后,您需要将该包的引导程序添加到您的应用程序中:
public function defineBootloaders(): array
{
return [
// ...
\Spiral\Scheduler\Bootloader\SchedulerBootloader::class,
// ...
];
}
在 Framework — Bootloaders 部分阅读有关引导程序的更多信息。
要运行计划任务,您有两种选择:
schedule:run
命令。* * * * * cd /path-to-your-project && php app.php schedule:run >> /dev/null 2>&1
schedule:work
命令通过 RoadRunner 运行计划任务。此命令将在前台运行,并每分钟调用调度程序,直到您终止该命令:php app.php schedule:work
您可以使用任何守护进程来保持该进程在后台模式下运行,例如 supervisord。
要在您的项目中使用 Scheduler 包,您可以创建一个新的 SchedulerBootloader
引导程序。
警告 不要忘记在您的应用程序中注册
SchedulerBootloader
!
此引导程序将负责管理 cron 作业任务:
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Scheduler\Schedule;
use Psr\Log\LoggerInterface;
use Spiral\Boot\DirectoriesInterface;
final class SchedulerBootloader extends Bootloader
{
public function boot(Schedule $schedule, DirectoriesInterface $dirs): void
{
// 按名称运行控制台命令
$schedule->command('ping', ['https://google.com'])
->everyFiveMinutes()
->withoutOverlapping()
->appendOutputTo($dirs->get('runtime').'logs/cron.log');
// 按类名运行控制台命令
$schedule->command(PingCommand::class, ['https://google.com'])
->everyFiveMinutes()
->withoutOverlapping()
->appendOutputTo($dirs->get('runtime').'logs/cron.log');
// 运行可调用任务
$schedule->call('Ping url', static function (LoggerInterface $logger, string $url) {
$headers = @get_headers($url);
$status = $headers && \strpos($headers[0], '200');
$logger->info(\sprintf('URL: %s %s', $url, $status ? 'Exists' : 'Does not exist'));
return $status;
}, ['url' => 'https://google.com'])
->everyFiveMinutes()
->withoutOverlapping();
}
}
此示例演示了如何使用 Scheduler 包来安排不同类型的命令以在特定的时间间隔运行,以及如何配置计划任务以防止重叠并记录输出。
现在,您可以检查是否已注册 cron 作业任务:
php app.php schedule:list
+--------------------------------------------------------+-------------+-------------+----------------------------+| [32m Command [39m| [32m Interval [39m| [32m Description [39m| [32m Next Due [39m|+--------------------------------------------------------+-------------+-------------+----------------------------+| /usr/bin/php8.1 app.php ping 'https://google.com' | */5 * * * * | | 2023-01-12 12:55:00 +00:00 || /usr/bin/php8.1 app.php ping:site 'https://google.com' | */5 * * * * | Ping site | 2023-01-12 12:55:00 +00:00 || callback: 'url' | */5 * * * * | Ping url | 2023-01-12 12:55:00 +00:00 |+--------------------------------------------------------+-------------+-------------+----------------------------+
您可以使用 before
、then
、when
和 skip
方法注册回调。
before
方法可用于注册一个回调,该回调将在计划任务执行之前被调用。此回调可用于在任务运行之前执行任何必要的准备或设置。
$schedule->command('backup:run')
->everyFiveMinutes()
->before(static fn(Notifier $notifier) => $notifier->send('Starting backup...'));
-> ...;
then
方法可用于注册一个回调,该回调将在计划任务执行之后被调用。此回调可用于在任务运行之后执行任何必要的清理或附加处理。
$schedule->command('backup:run')
->everyFiveMinutes()
->then(static fn(Notifier $notifier) => $notifier->send('Backup completed'));
-> ...;
when
方法可用于注册一个回调,该回调将被调用以确定是否应该执行计划任务。此回调可用于检查任何可能阻止任务运行的约束条件。如果 when 回调返回 false
,则不会执行计划任务。
$schedule->command('email:digest')
->everyFiveMinutes()
->when(static fn(HolidaysCalendar $calendar) => !$calendar->isHoliday());
-> ...;
skip
方法可用于注册一个回调,该回调将被调用以确定是否应该跳过计划任务。此回调可用于检查任何可能阻止任务运行的条件。如果 skip 回调返回 true
,则不会执行计划任务。
$schedule->command('email:digest')
->everyFiveMinutes()
->skip(static fn(HolidaysCalendar $calendar) => $calendar->isHoliday());
-> ...;
您可以使用 runInBackground()
方法在后台运行任务:
$schedule->command('ping', ['https://google.com'])
->everyFiveMinutes()
->runInBackground()
-> ...;
警告 不要让可调用任务在后台运行。这将导致错误。
当使用 withoutOverlapping()
方法时,该包将在调度其新实例之前检查该命令或函数是否已在运行。如果该命令或函数的一个实例已在运行,则不会调度新实例,并且将被跳过。这有助于防止同一命令或函数的多个实例同时运行并可能导致问题。
$schedule->command('ping', ['https://google.com'])
->everyMinute()
->withoutOverlapping()
-> ...;
除了该方法的默认行为外,您还可以指定一个自定义的锁定过期时间(以分钟为单位)。这可以通过将一个整数值作为参数传递给该方法来完成。
$schedule->command('ping', ['https://google.com'])
->everyMinute()
->withoutOverlapping(60)
-> ...;
注意 默认情况下,锁定过期时间为 24 小时。
您可以使用 expression 参数为任务分配自定义的 cron 表达式。
例如,以下代码为 email:send
命令分配了 cron 表达式 * * * * *
(它对应于每分钟运行):
$schedule->command('email:send', expression: new \Cron\CronExpression('* * * * *'));
此外,您可以使用辅助方法以更易于阅读的方式定义您的计划任务。这些方法包括:
方法 | 描述 |
---|---|
everyMinute() |
每分钟: * * * * * |
everyEvenMinute() |
每偶数分钟: */2 * * * * |
everyFiveMinutes() |
每五分钟: */5 * * * * |
everyTenMinutes() |
每十分钟: */10 * * * * |
everyFifteenMinutes() |
每十五分钟: */15 * * * * |
everyThirtyMinutes() |
每三十分钟: 0,30 * * * * |
注意 更多关于如何安排任务以在特定分钟运行的示例,你可以在 这里 找到。
方法 | 描述 |
---|---|
hourly() |
每小时的 00 分钟: 0 * * * * |
hourlyAt(15) |
每小时的指定分钟: 15 * * * * |
hourlyAt(15, 30, 45) |
每小时的 15、30、45 分钟: 15,30,45 * * * * |
everyTwoHours() |
每天的 00:00: 0 0 * * * |
注意 更多关于如何安排任务以按小时运行的示例,你可以在 这里 找到。
方法 | 描述 |
---|---|
daily() |
每天的 00:00: 0 0 * * * |
daily(13) |
每天的指定时间: 0 13 * * * |
daily(3, 15, 23) |
每天的 03:00、15:00、23:00: 0 3,15,23 * * * |
twiceDaily(1, 13) |
每天的 01:00、13:00: 0 1,13 * * * |
lastDayOfMonth() |
每月的最后一天 00:00: 0 0 L * * |
lastDayOfMonth(12) |
每月的最后一天 12:00: 0 12 L * * |
lastDayOfMonth(12, 30) |
每月的最后一天 12:30: 30 12 L * * |
lastWeekdayOfMonth() |
每月的最后一个工作日 00:00: 0 0 LW * * |
lastWeekdayOfMonth(12) |
每月的最后一个工作日 12:00: 0 12 LW * * |
lastWeekdayOfMonth(12, 30) |
每月的最后一个工作日 12:30: 30 12 LW * * |
注意 更多关于如何安排任务以按天运行的示例,你可以在 这里 找到。
方法 | 描述 |
---|---|
weekly() |
每周一: 0 0 * * 0 |
注意 更多关于如何安排任务以每周运行的示例,你可以在 这里 找到。
方法 | 描述 |
---|---|
monthly() |
每月 1 日 00:00: 0 0 1 * * |
monthly(12) |
每月 1 日 12:00: 0 12 1 * * |
monthly(12, 30) |
每月 1 日 12:30: 30 12 1 * * |
monthlyOn(15, 12) |
每月 15 日 12:00: 0 12 15 * * |
monthlyOn(15, 12, 30) |
每月 15 日 12:30: 30 12 15 * * |
quarterly() |
每季度 yyyy-01,03,06,09-01 00:00: 0 0 1 1-12/3 * |
yearly() |
每年 yyyy-01-01 00:00: 0 0 1 1 * |
注意 更多关于如何安排任务以按月运行的示例,你可以在 这里 找到。
使用 schedule:list
命令列出所有已调度的作业:
php app.php schedule:list
使用 schedule:work
命令启动计划任务工作进程:
php app.php schedule:work