Laravel Cron调度和它的秘密
如果cron任务的数量增加, 或者他们的工作变得过于繁重, 有一些内部行为你需要知道以避免大的头痛.
大家好, 我是Valerio Barbera, 软件工程师, 创始人, 以及Inspector的CTO.
Laravel最有用的功能之一是任务调度系统。官方文档清楚地解释了它的作用。
在过去, 你可能为你需要在服务器上安排的每个任务写一个cron配置条目.然而, 这很快就会成为一种痛苦, 因为你的任务调度不再是在源控制中, 你必须通过SSH进入你的服务器来查看你现有的cron条目或添加额外的条目.
Laravel的命令调度器提供了一个全新的方法来管理你服务器上的计划任务.调度器允许你在你的Laravel应用程序本身流畅地和表达地定义你的命令时间表。当使用调度器时, 在你的服务器上只需要一个单一的cron条目.
一些常见的调度任务的使用情况:
- 每日/每周/每月的总结报告
- 垃圾收集
- 导入/导出过程
- 通知客户即将到期的信息(账户、信用卡等)。
我自己对该框架的这个组成部分做了很多工作,因为Inspector后台系统的一些部分依赖于它。
实验第一个任务可以给你带来很多快乐, 但是当任务的数量增加, 或者他们的内部工作变得太重, 有一些非直观的行为你需要知道,以避免以后大的头痛,并准备好一个可持续的应用增长.
Laravel调度是如何工作的
Laravel任务调度的设计就像你的cron调度列表的代理。你只需要在你的服务器中的cron文件上有一行。
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
它指示cron调度器每分钟运行这个artisan命令。
不用太深入,分析一下\Illuminate\Console\Scheduling\ScheduleRunCommand 类的实现,你就可以看到,它迭代了定义在 **App\Console\Kernel**类中定义的、准备在foreach 循环中执行的事件,并通过runEvent 方法运行它们。
PHP
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
//...
$this->runEvent($event);
//...
}
它负责根据你为每个命令配置的频率,执行所有在App\Console\Kernel 类中定义的命令。
如何安排一个任务
想象一下,你需要每10分钟检查一次你的博客文章,如果它们无法访问,就发送一个警报。你可以安排这个命令。
PHP
namespace App\Console;
use App\Console\Commands\PostsCehckCommand;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command(PostsCheckCommand::class)->everyTenMinutes();
}
}
Laravel调度器提供了流畅和富有表现力的API,帮助你定义一个命令必须运行的方式。
每次你需要运行一个任务时, 你可以在调度器中添加一个新的行来定义应该执行什么任务和它的频率.
平行执行
检查一下该类的代码 **ScheduleRunCommand**类的代码,默认情况下,在同一时间安排的多个任务将根据它们在你的时间表方法中定义的顺序依次执行。
该命令使用foreach 循环来迭代准备执行的事件,所以如果你有长期运行的任务,或者任务列表很长,这可能会导致后续任务的启动时间比预期的晚很多。
为了模拟这种情况,我创建了两个命令。
SleepCommand(包含五秒钟的睡眠)AnotherCommand(即简单地写一个新的日志行)
然后将相应的行添加到调度程序中。
PHP
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command(SleepCommand::class)->everyMinute();
$schedule->command(AnotherCommand::class)->everyMinute();
}
在你的终端中输入下面的命令来运行测试。
php artisan schedule:run
日志将报告这两行,间隔时间为五秒。
如果你喜欢在后台运行任务,以便它们可以同时运行,你可以使用runInBackground ,以防止任务之间相互踩踏。
PHP
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command(SleepCommand::class)->everyMinute()->runInBackground();
$schedule->command(AnotherCommand::class)->everyMinute();
}
日志证实,睡眠对第二个命令的运行时间没有任何影响。
我个人在每个任务中默认使用runInBackground 选项。
不要担心异常情况的发生
由于命令是通过foreach 循环按顺序执行的,你可能会担心一个命令的异常会让整个循环停止。
幸运的是,情况并非如此。
ScheduleRunCommand 只是通过Laravel的异常处理程序报告异常,而不会中断循环,所以列表中的下一个命令可以按预期执行。
PHP
/**
* Run the given event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @return void
*/
protected function runEvent($event)
{
$this->dispatcher->dispatch(new ScheduledTaskStarting($event));
try {
$event->run($this->laravel);
$this->dispatcher->dispatch(new ScheduledTaskFinished($event);
} catch (Throwable $e) {
$this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e));
$this->handler->report($e);
}
}
可见性
预定任务就像你的应用程序的一个隐藏部分, 因为它们远离用户的眼睛.它们不像你在控制器中写的代码那样与用户交互联系在一起。
它们的存在促使你不断检查日志(甚至在星期六和星期天),以确保没有错误出现。
如果在HTTP请求过程中出现问题,会导致红色气泡或信息,立即告知用户问题。在生产中发布软件之前,自己使用该应用程序,很容易发现相关错误。
然而,如果一个预定的命令失败了,它就会悄无声息地进行,没有人注意到。
检查员的设计是为了消除你日常工作中的这些顾虑。它与一个轻量级的软件库一起工作,你可以根据你用来开发后端的技术,像其他依赖一样安装在你的应用程序中。在我们的GitHub上查看支持的技术(https://github.com/inspector-apm).
经Valerio Barbera许可发表于DZone。点击这里查看原文。