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

任务调度

介绍

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

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

启动调度器

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

php
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

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

定义调度

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

php
<?php

namespace App\Console;

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

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

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

除了使用 Closures 调度,您还可以使用 可调用对象。可调用对象是包含 __invoke 方法的简单 PHP 类:

php
$schedule->call(new DeleteRecentUsers)->daily();

调度 Artisan 命令

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

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

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

调度队列任务

可以使用 job 方法调度 队列任务。此方法提供了一种方便的方法来调度任务,而无需使用 call 方法手动创建队列任务的 Closures:

php
$schedule->job(new Heartbeat)->everyFiveMinutes();

// 将任务派发到 "heartbeats" 队列...
$schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();

调度 Shell 命令

可以使用 exec 方法向操作系统发出命令:

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

调度频率选项

您可以为任务分配多种调度:

方法描述
->cron('* * * * *');按自定义 Cron 调度运行任务
->everyMinute();每分钟运行任务
->everyFiveMinutes();每五分钟运行任务
->everyTenMinutes();每十分钟运行任务
->everyFifteenMinutes();每十五分钟运行任务
->everyThirtyMinutes();每三十分钟运行任务
->hourly();每小时运行任务
->hourlyAt(17);每小时在 17 分钟时运行任务
->daily();每天午夜运行任务
->dailyAt('13:00');每天 13:00 运行任务
->twiceDaily(1, 13);每天 1:00 和 13:00 运行任务
->weekly();每周日 00:00 运行任务
->weeklyOn(1, '8:00');每周一 8:00 运行任务
->monthly();每月第一天 00:00 运行任务
->monthlyOn(4, '15:00');每月 4 日 15:00 运行任务
->quarterly();每季度第一天 00:00 运行任务
->yearly();每年第一天 00:00 运行任务
->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();限制任务在工作日运行
->weekends();限制任务在周末运行
->sundays();限制任务在周日运行
->mondays();限制任务在周一运行
->tuesdays();限制任务在周二运行
->wednesdays();限制任务在周三运行
->thursdays();限制任务在周四运行
->fridays();限制任务在周五运行
->saturdays();限制任务在周六运行
->between($start, $end);限制任务在开始和结束时间之间运行
->when(Closure);基于真值测试限制任务
->environments($env);限制任务在特定环境中运行

时间约束

可以使用 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 时执行。

环境约束

可以使用 environments 方法仅在给定环境中执行任务:

php
$schedule->command('emails:send')
            ->daily()
            ->environments(['staging', 'production']);

时区

使用 timezone 方法,您可以指定计划任务的时间应在给定时区内解释:

php
$schedule->command('report:generate')
         ->timezone('America/New_York')
         ->at('02:00')

如果您为所有计划任务分配相同的时区,您可能希望在 app/Console/Kernel.php 文件中定义一个 scheduleTimezone 方法。此方法应返回应分配给所有计划任务的默认时区:

php
/**
 * 获取应默认用于计划事件的时区。
 *
 * @return \DateTimeZone|string|null
 */
protected function scheduleTimezone()
{
    return 'America/Chicago';
}

NOTE

请记住,某些时区使用夏令时。当夏令时变化发生时,您的计划任务可能会运行两次甚至根本不运行。因此,我们建议尽可能避免使用时区调度。

防止任务重叠

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

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

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

如果需要,您可以指定在 "无重叠" 锁过期之前必须经过多少分钟。默认情况下,锁将在 24 小时后过期:

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

在一台服务器上运行任务

NOTE

要使用此功能,您的应用程序必须使用 memcachedredis 缓存驱动作为应用程序的默认缓存驱动。此外,所有服务器必须与同一个中央缓存服务器通信。

如果您的应用程序在多台服务器上运行,您可以限制计划任务仅在一台服务器上执行。例如,假设您有一个计划任务,每周五晚上生成一个新报告。如果任务调度器在三台工作服务器上运行,计划任务将在所有三台服务器上运行并生成三次报告。这不好!

要指示任务仅在一台服务器上运行,请在定义计划任务时使用 onOneServer 方法。第一个获取任务的服务器将对作业获得一个原子锁,以防止其他服务器同时运行相同的任务:

php
$schedule->command('report:generate')
                ->fridays()
                ->at('17:00')
                ->onOneServer();

后台任务

默认情况下,同时计划的多个命令将按顺序执行。如果您有长时间运行的命令,这可能会导致后续命令的启动时间比预期的要晚得多。如果您希望命令在后台运行,以便它们可以同时运行,可以使用 runInBackground 方法:

php
$schedule->command('analytics:report')
         ->daily()
         ->runInBackground();

NOTE

runInBackground 方法仅可用于通过 commandexec 方法调度任务时使用。

维护模式

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 方法,您可以将输出通过电子邮件发送到您选择的电子邮件地址。在通过电子邮件发送任务的输出之前,您应该配置 Laravel 的 电子邮件服务

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

如果您只希望在命令失败时通过电子邮件发送输出,请使用 emailOutputOnFailure 方法:

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

NOTE

emailOutputToemailOutputOnFailuresendOutputToappendOutputTo 方法仅适用于 commandexec 方法。

任务钩子

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

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

onSuccessonFailure 方法允许您指定在计划任务成功或失败时执行的代码:

php
$schedule->command('emails:send')
         ->daily()
         ->onSuccess(function () {
             // 任务成功...
         })
         ->onFailure(function () {
             // 任务失败...
         });

Pinging URLs

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

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

pingBeforeIfthenPingIf 方法可用于仅在给定条件为 true 时 ping 给定的 URL:

php
$schedule->command('emails:send')
         ->daily()
         ->pingBeforeIf($condition, $url)
         ->thenPingIf($condition, $url);

pingOnSuccesspingOnFailure 方法可用于仅在任务成功或失败时 ping 给定的 URL:

php
$schedule->command('emails:send')
         ->daily()
         ->pingOnSuccess($successUrl)
         ->pingOnFailure($failureUrl);

所有 ping 方法都需要 Guzzle HTTP 库。您可以使用 Composer 包管理器将 Guzzle 添加到您的项目中:

php
composer require guzzlehttp/guzzle