Skip to content
赞助商
虚位以待
赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

任务调度

介绍

过去,您可能为每个需要在服务器上调度的任务生成一个 Cron 条目。然而,这很快就会变得麻烦,因为您的任务调度不再在源代码控制中,您必须 SSH 到服务器中以添加额外的 Cron 条目。

Laravel 的命令调度器允许您在 Laravel 内部流畅且富有表现力地定义命令调度。在使用调度器时,您的服务器上只需要一个 Cron 条目。您的任务调度是在 app/Console/Kernel.php 文件的 schedule 方法中定义的。为了帮助您入门,该方法中定义了一个简单的示例。

启动调度器

使用调度器时,您只需在服务器上添加以下 Cron 条目。如果您不知道如何在服务器上添加 Cron 条目,可以考虑使用 Laravel Forge 这样的服务来管理 Cron 条目:

php
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1

这个 Cron 将每分钟调用 Laravel 命令调度器。当执行 schedule:run 命令时,Laravel 将评估您计划的任务并运行到期的任务。

定义调度

您可以在 App\Console\Kernel 类的 schedule 方法中定义所有计划任务。首先,让我们看一个调度任务的示例。在这个示例中,我们将计划一个 Closure 每天午夜被调用。在 Closure 中,我们将执行一个数据库查询以清空一个表:

php
<?php

namespace App\Console;

use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * 应用程序提供的 Artisan 命令。
     *
     * @var array
     */
    protected $commands = [
        \App\Console\Commands\Inspire::class,
    ];

    /**
     * 定义应用程序的命令调度。
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }
}

除了调度 Closure 调用,您还可以调度 Artisan 命令 和操作系统命令。例如,您可以使用 command 方法通过命令的名称或类来调度 Artisan 命令:

php
$schedule->command('emails:send --force')->daily();

$schedule->command(EmailsCommand::class, ['--force'])->daily();

exec 命令可用于向操作系统发出命令:

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

调度频率选项

当然,您可以为任务分配各种调度:

方法描述
->cron('* * * * * *');在自定义 Cron 调度上运行任务
->everyMinute();每分钟运行任务
->everyFiveMinutes();每五分钟运行任务
->everyTenMinutes();每十分钟运行任务
->everyThirtyMinutes();每三十分钟运行任务
->hourly();每小时运行任务
->hourlyAt(17);每小时在 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');设置时区

这些方法可以与其他约束结合使用,以创建更精细的调度,仅在一周的某些天运行。例如,要计划一个命令在每周一运行:

php
// 每周一下午 1 点运行一次...
$schedule->call(function () {
    //
})->weekly()->mondays()->at('13:00');

// 在工作日的上午 8 点到下午 5 点之间每小时运行一次...
$schedule->command('foo')
          ->weekdays()
          ->hourly()
          ->timezone('America/Chicago')
          ->between('8:00', '17:00');

以下是其他调度约束的列表:

方法描述
->weekdays();限制任务在工作日运行
->sundays();限制任务在周日运行
->mondays();限制任务在周一运行
->tuesdays();限制任务在周二运行
->wednesdays();限制任务在周三运行
->thursdays();限制任务在周四运行
->fridays();限制任务在周五运行
->saturdays();限制任务在周六运行
->between($start, $end);限制任务在开始和结束时间之间运行
->when(Closure);基于真值测试限制任务

时间间隔约束

between 方法可用于根据一天中的时间限制任务的执行:

php
$schedule->command('reminders:send')
                    ->hourly()
                    ->between('7:00', '22:00');

类似地,unlessBetween 方法可用于排除任务在一段时间内的执行:

php
$schedule->command('reminders:send')
                    ->hourly()
                    ->unlessBetween('23:00', '4:00');

真值测试约束

when 方法可用于根据给定真值测试的结果限制任务的执行。换句话说,如果给定的 Closure 返回 true,任务将执行,只要没有其他约束条件阻止任务运行:

php
$schedule->command('emails:send')->daily()->when(function () {
    return true;
});

skip 方法可以看作是 when 的反义词。如果 skip 方法返回 true,则计划的任务将不会执行:

php
$schedule->command('emails:send')->daily()->skip(function () {
    return true;
});

使用链式 when 方法时,计划的命令只有在所有 when 条件返回 true 时才会执行。

防止任务重叠

默认情况下,即使上一个任务实例仍在运行,计划任务也会运行。要防止这种情况,您可以使用 withoutOverlapping 方法:

php
$schedule->command('emails:send')->withoutOverlapping();

在这个示例中,emails:send Artisan 命令 将每分钟运行一次,如果它尚未运行。withoutOverlapping 方法在您有执行时间变化很大的任务时特别有用,防止您无法准确预测给定任务需要多长时间。

维护模式

Laravel 的计划任务在 Laravel 处于维护模式时不会运行,因为我们不希望您的任务干扰您可能正在服务器上执行的任何未完成的维护。然而,如果您希望在维护模式下强制运行任务,可以使用 evenInMaintenanceMode 方法:

php
$schedule->command('emails:send')->evenInMaintenanceMode();

任务输出

Laravel 调度器提供了几个方便的方法来处理计划任务生成的输出。首先,使用 sendOutputTo 方法,您可以将输出发送到文件以供以后检查:

php
$schedule->command('emails:send')
         ->daily()
         ->sendOutputTo($filePath);

如果您希望将输出附加到给定文件,可以使用 appendOutputTo 方法:

php
$schedule->command('emails:send')
         ->daily()
         ->appendOutputTo($filePath);

使用 emailOutputTo 方法,您可以将输出通过电子邮件发送到您选择的电子邮件地址。请注意,输出必须首先使用 sendOutputTo 方法发送到文件。在通过电子邮件发送任务的输出之前,您应该配置 Laravel 的电子邮件服务

php
$schedule->command('foo')
         ->daily()
         ->sendOutputTo($filePath)
         ->emailOutputTo('foo@example.com');

NOTE

emailOutputTosendOutputToappendOutputTo 方法仅适用于 command 方法,不支持 call

任务钩子

使用 beforeafter 方法,您可以指定在计划任务完成之前和之后执行的代码:

php
$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // 任务即将开始...
         })
         ->after(function () {
             // 任务完成...
         });

URL 监控

使用 pingBeforethenPing 方法,调度器可以在任务完成之前或之后自动 ping 给定的 URL。此方法对于通知外部服务(如 Laravel Envoyer)您的计划任务正在开始或已完成执行非常有用:

php
$schedule->command('emails:send')
         ->daily()
         ->pingBefore($url)
         ->thenPing($url);

使用 pingBefore($url)thenPing($url) 功能需要 Guzzle HTTP 库。您可以使用 Composer 包管理器将 Guzzle 添加到您的项目中:

php
composer require guzzlehttp/guzzle