Laravel提供了非常强劲的命令行工具(如果还不了解,传送到第8往篇),我们如果想要定期执行某个命令行,可以利用crontab进行定时设置。如果有多个定期的任务,很简单,我们设定多条crontab规则在不同时间段内调度不同的命令即可。
而Laravel框架内部也提供了这样的调度机制。(当然,还是逃不过crontab的支持)
首先,我们需要在crontab中添加这么一条规则
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
其次,所有的调度任务都在/app/Console/Kernel.php的schedule函数中进行调度设置
这个函数中可以调度闭包、可调用对象(包含__invoke的对象)、artisian命令、列队任务以及shell命令。以下以例子进行说明:
//调度闭包,->daily()即调度选项,每天凌晨零点运行任务 //关于调度选项后面会统一说明 $schedule->call(function () { DB::table('recent_users')->delete(); })->daily(); //调度可调用对象 //DeleteRecentUsers类中包含了__invoke魔术方法 $schedule->call(new DeleteRecentUsers)->daily(); //调度artisian命令 $schedule->command('emails:send --force')->daily(); $schedule->command(EmailsCommand::class, ['--force'])->daily(); //调度列队任务 $schedule->job(new Heartbeat)->everyFiveMinutes(); $schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes(); //将job投递到heartbeats列队中 //调度shell命令 $schedule->exec('node /home/forge/script.js')->daily();
常用的调度选项:
方法 | 描述 |
->cron('* * * * *'); | 在自定义Cron调度上运行任务 |
->everyMinute(); | 每分钟运行一次任务 |
->everyFiveMinutes(); | 每五分钟运行一次任务 |
->everyTenMinutes(); | 每十分钟运行一次任务 |
->everyFifteenMinutes(); | 每十五分钟运行一次任务 |
->everyThirtyMinutes(); | 每三十分钟运行一次任务 |
->hourly(); | 每小时运行一次任务 |
->hourlyAt(17); | 每小时第十七分钟运行一次任务 |
->daily(); | 每天凌晨零点运行任务 |
->dailyAt('13:00'); | 每天13:00运行任务 |
->twiceDaily(1, 13); | 每天1:00 & 13:00运行任务 |
->weekly(); | 每周运行一次任务 |
->monthly(); | 每月运行一次任务 |
->monthlyOn(4, '15:00'); | 每月4号15:00运行一次任务 |
->quarterly(); | 每个季度运行一次 |
->yearly(); | 每年运行一次 |
->timezone('America/New_York'); | 设置时区 |
额外调度约束选项:
方法 | 描述 |
->weekdays(); | 只在工作日运行任务 |
->weekends(); | 只在周末运行任务 |
->sundays(); | 每个星期天运行任务 |
->mondays(); | 每个星期一运行任务 |
->tuesdays(); | 每个星期二运行任务 |
->wednesdays(); | 每个星期三运行任务 |
->thursdays(); | 每个星期四运行任务 |
->fridays(); | 每个星期五运行任务 |
->saturdays(); | 每个星期六运行任务 |
->between($start, $end); | 基于特定时间段运行任务 |
->when(Closure); | 基于特定测试运行任务 |
->environments($env); | 只在指定环境运行任务 |
举几个例子:
// 每周星期一13:00运行一次... $schedule->call(function () { })->weekly()->mondays()->at('13:00'); // 工作日的上午8点到下午5点每小时运行... $schedule->command('foo') ->weekdays() ->hourly() ->timezone('America/Chicago') ->between('8:00', '17:00'); //每天7点到22点之间,每小时运行 $schedule->command('reminders:send') ->hourly() ->between('7:00', '22:00'); //每天23点到4点之外的时间内,每小时运行 $schedule->command('reminders:send') ->hourly() ->unlessBetween('23:00', '4:00'); //when的闭包返回true时运行 $schedule->command('emails:send')->daily()->when(function () { return true; }); //skip的闭包返回false时运行 $schedule->command('emails:send')->daily()->skip(function () { return true; });
第三,任务调度的重叠处理
当下一次任务开始执行时,万一上一次的任务还没有执行完成,新的任务是会执行的。也就是说,有可能出现多次任务重叠执行的情况。
为了避免这种情况,我们可以使用外部的手段来加锁,比如文件锁flock,或者redis的setnx都可以。但是Laravel非常贴心,已经帮我们准备了这样的机制:
$schedule->command('emails:send')->withoutOverlapping(); //锁默认24小时失效 $schedule->command('emails:send')->withoutOverlapping(10); //锁10分钟失效
第四,任务调度的顺序
默认情况下,同时调度的多个命令会顺序执行。如果你有一些长时间运行的命令,将会导致随后的命令在预期之后很久才能执行。如果你想要让命令在后台执行以便它们可以同时运,可以使用 runInBackground 方法来实现:
$schedule->command('analytics:report') ->daily() ->runInBackground();
第五,任务输出
我们可以将任务中的输出重定向覆盖写入或追加写入文件中,便于问题的排查:
//覆盖写入 $schedule->command('emails:send') ->daily() ->sendOutputTo($filePath); //追加写入 $schedule->command('emails:send') ->daily() ->appendOutputTo($filePath);
最后,任务钩子
使用 before 和 after 方法,你可以指定在调度任务完成之前和之后要执行的代码:
$schedule->command('emails:send') ->daily() ->before(function () { // 任务即将开始... }) ->after(function () { // 任务已经完成... });
好了,Laravel话题到这里正式结束了。关于Laravel还有一些知识点,如果想要深入了解的小伙伴就去看官方文档吧。
下周开始,博主会进入另一个话题。至于要写什么,嗯,还没想好,大家下周再见:)
--------------------------- 我是可爱的分割线 ----------------------------
最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。