Любите загадки? Событие еще доступно на сайте.
Поддержите нас - каждый вклад важен!
Поддержите нас - каждый вклад важен!

Планировщик задач

Введение

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

Планировщик команд Laravel позволяет вам гибко и выразительно определить планирование своих команд в самом Laravel. И для этого на вашем сервере необходима только одна Cron-запись. Ваш планировщик задач определён в методе schedule файла app/Console/Kernel.php. Чтобы помочь вам начать, там уже есть простой пример с методом.

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

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

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

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

Определение планировщика

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

<?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-команду, используя либо имя команды, либо класс:

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

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

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

$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'); Задать часовой пояс

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

// Запуск каждый понедельник в 13:00...
$schedule->call(function () {
    //
})->weekly()->mondays()->at('13:00');

// Запускать каждый час с 8:00 до 17:00 по будням...
$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 можно ограничить выполнение задачи в зависимости от времени дня:

$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.

Предотвращение перекрытий задач

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

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

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

Режим обслуживания

Запланированные задачи 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, вы можете отправить по электронной почте выходные данные на адрес по вашему усмотрению. Перед отправкой на электронную почту результата выполнения задачи вы должны настроить e-mail сервисы Laravel:

$schedule->command('foo')
         ->daily()
         ->sendOutputTo($filePath)
         ->emailOutputTo('[email protected]');

Методы emailOutputTo, sendOutputTo и appendOutputTo доступны исключительно для метода command и не поддерживаются для call.

Хуки задач

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

$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // Задача почти началась...
         })
         ->after(function () {
             // Задача завершена...
         });

Пинг URL

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

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

Использование функций pingBefore($url) или thenPing($url) требует библиотеки Guzzle HTTP. Вы можете добавить Guzzle к вашему проекту с помощью менеджера пакетов Composer:

composer require guzzlehttp/guzzle