laravel-任务调度

laravel schedule

开头先说一下平时在项目中定时任务的写法:平时我们如果需要定时地去更新或执行一些操作,我们是通过写crontab去调用php脚本、shell脚本等定时去完成指定操作,例如这样

但是带来的一个问题就是 当项目比较多的时候,各个项目的定时任务都怼在一起,你很难区分这些定时任务,再者,添加多个定时任务需要你去频繁的利用ssh登录服务器去添加crontab任务,很是让人头疼;而在laravel中 框架就提供了任务调度器去系统且优雅地进行任务调度,我们只需要每分钟去执行框架的schedule:run命令即可,它每分钟会去扫描你的定时操作并定期执行,具体调用操作如下

定义调度

你只需要在app/Console/Kernel的schedule方法中去定义需要执行的操作即可,比如:下面的操作

你可以像上面的例子一样调用artisancommand去执行操作

也可以去调用像这样的闭包去执行

1
2
3
4
5
6
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }

亦或者直接用exec去执行系统脚本

$schedule->exec('node /home/forge/script.js')->daily();

调用脚本的频次

你可以用如下的框架命令去执行

方法 描述
->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(); 每年运行一次

也可以调用->cron去自定义执行时间(像这样20 0 * * *)

查看任务输出

为了方便查看调度任务的执行结果,平时我们是这样

去把执行结果在代码中输出 并把输出保存在日志文件中方便查看,laravel中我们可以用sendOutputTo将输出结果保存到指定文件或者用appendOutputTo追加输出结果到指定文件,甚至可以用emailOutputTo发送邮件通知某人执行结果

任务钩子

使用 before 和 after方法,你可以指定在调度任务完成之前和之后要执行的代码:

1
2
3
4
5
6
7
8
$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             # 任务即将开始...
         })
         ->after(function () {
             #任务已经完成...
         });

还有像这样的操作

平时low一点的写法 你完全可以用if else去代替这里的操作,不过框架为我们提供了这样系统且优雅的写法 在框架中为:\vendor\laravel\framework\src\Illuminate\Foundation\Providers\ArtisanServiceProvider.php这个文件中会去注册框架自带的包括schedule:run的commands, 而scheduleRun的具体实现是在\vendor\laravel\framework\src\Illuminate\Console\Scheduling\ScheduleRunCommand.php文件中,是这样的一段代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Execute the console command.
 *
 * @return void
 */
public function fire()
{
 $eventsRan = false;

 foreach ($this->schedule->dueEvents($this->laravel) as $event) {
 if (! $event->filtersPass($this->laravel)) {
 continue;
 }

 $this->line('<info>Running scheduled command:</info> '.$event->getSummaryForDisplay());

 $event->run($this->laravel);

 $eventsRan = true;
 }

 if (! $eventsRan) {
 $this->info('No scheduled commands are ready to run.');
 }
}

它会获取所有的event 并循环执行,像这样

event类在\vendor\laravel\framework\src\Illuminate\Console\Scheduling\Event.php中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * Run the given event.
 *
 * @param  \Illuminate\Contracts\Container\Container  $container
 * @return void
 */
public function run(Container $container)
{
    if ($this->withoutOverlapping &&
        ! $this->mutex->create($this)) {
        return;
    }

    $this->runInBackground
                ? $this->runCommandInBackground($container)
                : $this->runCommandInForeground($container);
}

这里拿前台执行来说明

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * Run the command in the foreground.
 *
 * @param  \Illuminate\Contracts\Container\Container  $container
 * @return void
 */
protected function runCommandInForeground(Container $container)
{
    $this->callBeforeCallbacks($container);

    (new Process(
        $this->buildCommand(), base_path(), null, null, null
    ))->run();

    $this->callAfterCallbacks($container);
}

最后就是拿拼接的参数去组成crontab命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/**
 * Build the command for running the event in the foreground.
 *
 * @param  \Illuminate\Console\Scheduling\Event  $event
 * @return string
 */
protected function buildForegroundCommand(Event $event)
{
    $output = ProcessUtils::escapeArgument($event->output);

    return $this->ensureCorrectUser(
        $event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1'
    );
}

buildCommand的结果

还有一些部分没有介绍,可以参考http://laravelacademy.org/post/9000.html