Unetway

Laravel - Планирование задач

Вступление

Раньше вы могли создавать записи Cron для каждой задачи, которую вам нужно было запланировать на вашем сервере. Однако, это может быстро стать проблемой, потому что ваше расписание задач больше не находится под контролем исходного кода, и вы должны подключиться к серверу по SSH, чтобы добавить дополнительные записи Cron.

Планировщик команд Laravel позволяет вам свободно и выразительно определять расписание команд в самом Laravel. При использовании планировщика на вашем сервере требуется только одна запись Cron. Расписание вашей задачи определяется в методе файла . Чтобы помочь вам начать, в методе определен простой пример.app/Console/Kernel.phpschedule

Запуск планировщика

При использовании планировщика вам нужно всего лишь добавить следующую запись Cron на ваш сервер. Если вы не знаете, как добавить записи Cron на ваш сервер, рассмотрите возможность использования службы, такой как Laravel Forge, которая может управлять записями Cron для вас:

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

Этот Cron будет вызывать планировщик команд Laravel каждую минуту. Когда команда будет выполнена, Laravel оценит ваши запланированные задачи и выполнит задачи, которые должны быть выполнены.schedule:run

 

Определение расписаний

Вы можете определить все свои запланированные задачи в scheduleметоде класса. Для начала давайте рассмотрим пример планирования задачи. В этом примере мы планируем вызывать a каждый день в полночь. Внутри мы выполним запрос к базе данных, чтобы очистить таблицу:App\Console\KernelClosureClosure

<?php

namespace App\Console;

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

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }
}

Помимо планирования с помощью замыканий, вы также можете использовать вызываемые объекты . Вызываемые объекты - это простые классы PHP, которые содержат __invokeметод:

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

 

Планирование команд ремесленников

Помимо планирования вызовов Closure, вы также можете запланировать команды Artisan и команды операционной системы. Например, вы можете использовать commandметод для планирования команды Artisan, используя либо имя команды, либо класс:

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

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

 

Планирование заданий в очереди

Этот jobметод может использоваться для планирования работы в очереди . Этот метод обеспечивает удобный способ планирования заданий без использования callметода для создания замыканий вручную для постановки в очередь:

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

// Dispatch the job to the "heartbeats" queue...
$schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();

 

Планирование команд оболочки

Этот execметод может использоваться для выдачи команды операционной системе:

$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(); Запускать задание каждую неделю
->weeklyOn(1, '8:00'); Выполняйте задание каждую неделю в понедельник в 8:00.
->monthly(); Запускать задание каждый месяц
->monthlyOn(4, '15:00'); Выполняйте задание каждый месяц 4го числа в 15:00.
->quarterly(); Выполняйте задание каждую четверть
->yearly(); Запускать задачу каждый год
->timezone('America/New_York'); Установите часовой пояс

Эти методы могут быть объединены с дополнительными ограничениями для создания даже более точно настроенных расписаний, которые выполняются только в определенные дни недели. Например, чтобы запланировать выполнение команды еженедельно в понедельник:

// Run once per week on Monday at 1 PM...
$schedule->call(function () {
    //
})->weekly()->mondays()->at('13:00');

// Run hourly from 8 AM to 5 PM on weekdays...
$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метод может использоваться для ограничения выполнения задачи в зависимости от времени суток:

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

Точно так же unlessBetweenметод может использоваться, чтобы исключить выполнение задачи на период времени:

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

Ограничения теста правды

Этот whenметод может использоваться для ограничения выполнения задачи на основе результата данного теста истинности. Другими словами, если заданное значение Closureвозвращается true, задача будет выполняться до тех пор, пока никакие другие ограничивающие условия не препятствуют ее выполнению:

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

skipМетод может рассматриваться как обратное when. Если skipметод возвращается true, запланированное задание не будет выполнено:

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

При использовании цепных whenметодов запланированная команда будет выполняться только в случае whenвозврата всех условий true.

Ограничения окружающей среды

environmentsМетод может быть использован для выполнения задач только на заданных условиях:

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

 

Часовые пояса

Используя timezoneметод, вы можете указать, что время запланированной задачи должно интерпретироваться в пределах данного часового пояса:

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

Если вы назначаете один и тот же часовой пояс всем запланированным задачам, вы можете определить scheduleTimezoneметод в своем файле. Этот метод должен возвращать часовой пояс по умолчанию, который должен быть назначен всем запланированным задачам:app/Console/Kernel.php

/**
 * Get the timezone that should be used by default for scheduled events.
 *
 * @return \DateTimeZone|string|null
 */
protected function scheduleTimezone()
{
    return 'America/Chicago';
}

Помните, что некоторые часовые пояса используют летнее время. Когда происходят изменения летнего времени, запланированное задание может выполняться дважды или даже не запускаться вообще. По этой причине мы рекомендуем избегать планирования часовых поясов, когда это возможно.

 

Предотвращение дублирования задач

По умолчанию запланированные задачи будут выполняться, даже если предыдущий экземпляр задачи все еще выполняется. Чтобы предотвратить это, вы можете использовать withoutOverlappingметод:

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

В этом примере команда Artisan будет выполняться каждую минуту, если она еще не запущена. Этот метод особенно полезен, если у вас есть задачи, которые сильно различаются по времени выполнения, что не позволяет вам точно предсказать, сколько времени займет выполнение данной задачи.emails:send withoutOverlapping

При необходимости вы можете указать, сколько минут должно пройти, пока не истечет блокировка «без наложения». По умолчанию срок действия блокировки истекает через 24 часа:

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

 

Выполнение задач на одном сервере

Чтобы использовать эту функцию, ваше приложение должно использовать драйвер кэширования memcachedили в redisкачестве драйвера кэша вашего приложения по умолчанию. Кроме того, все серверы должны взаимодействовать с одним и тем же сервером центрального кэша.

Если ваше приложение работает на нескольких серверах, вы можете ограничить выполнение запланированного задания только на одном сервере. Например, предположим, что у вас есть запланированное задание, которое генерирует новый отчет каждую пятницу вечером. Если планировщик заданий работает на трех рабочих серверах, запланированное задание будет выполнено на всех трех серверах и сгенерирует отчет три раза. Нехорошо!

Чтобы указать, что задача должна выполняться только на одном сервере, используйте onOneServerметод при определении запланированной задачи. Первый сервер, получивший задачу, обеспечит атомарную блокировку для задания, чтобы другие серверы не могли одновременно выполнить ту же задачу:

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

 

Фоновые задачи

По умолчанию несколько команд, запланированных одновременно, будут выполняться последовательно. Если у вас есть длительные команды, это может привести к тому, что последующие команды начнутся намного позже, чем ожидалось. Если вы хотите запускать команды в фоновом режиме, чтобы все они могли выполняться одновременно, вы можете использовать runInBackgroundметод:

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

 

Режим технического обслуживания

Запланированные задачи Laravel не будут выполняться, когда Laravel находится в режиме обслуживания , поскольку мы не хотим, чтобы ваши задачи мешали выполнению незаконченного обслуживания, которое вы можете выполнять на своем сервере. Однако, если вы хотите, чтобы задача выполнялась даже в режиме обслуживания, вы можете использовать evenInMaintenanceModeметод:

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

 

Вывод задачи

Планировщик Laravel предоставляет несколько удобных методов для работы с выводом, генерируемым запланированными задачами. Сначала, используя sendOutputToметод, вы можете отправить вывод в файл для последующей проверки:

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

Если вы хотите добавить вывод к данному файлу, вы можете использовать appendOutputToметод:

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

Используя этот emailOutputToметод, вы можете отправить результаты на электронный адрес по вашему выбору. Перед отправкой по электронной почте вывода задачи, вы должны настроить почтовые службы Laravel :

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

Если вы хотите отправить вывод по электронной почте только в случае сбоя команды, используйте emailOnFailureметод:

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

В emailOutputToemailOnFailuresendOutputToи appendOutputToметоды являются исключительными к commandи execметодам.

 

Крюки задач

Использование beforeи afterметоды, вы можете задать код , который будет выполняться до и после того , как запланированное задание завершено:

$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // Task is about to start...
         })
         ->after(function () {
             // Task is complete...
         });

В onSuccessи onFailureметоды позволяют задать код , который будет выполняться , если запланированное задание успешно или не:

$schedule->command('emails:send')
         ->daily()
         ->onSuccess(function () {
             // The task succeeded...
         })
         ->onFailure(function () {
             // The task failed...
         });

Проверка связи с URL

Используя pingBeforeи thenPingметоды, планировщик может автоматически пинговать заданный URL до или после того, как задача будет завершена. Этот метод полезен для уведомления внешней службы, такой как Laravel Envoyer , о том, что ваша запланированная задача начинается или закончила выполнение:

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

Эти pingBeforeIfи thenPingIfметоды могут использоваться для проверки заданного URL , только если данное условие true:

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

Эти pingOnSuccessи pingOnFailureметоды могут использоваться для проверки заданного URL только если задача успешно или не:

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

Все методы ping требуют HTTP-библиотеки Guzzle. Вы можете добавить Guzzle в свой проект, используя менеджер пакетов Composer:

composer require guzzlehttp/guzzle