Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Задачи по расписанию

Раз в сутки очистить старые записи, каждый понедельник отправить отчёт, каждые пять минут проверить оплаты. Раньше для каждой такой задачи писали строку в системном cron. Компонент Scheduler хранит расписание в коде приложения.

Установка

composer require symfony/scheduler

Scheduler работает поверх Messenger: расписание — источник сообщений, которые выполняет обычный обработчик.

Задача по cron-выражению

namespace App\Scheduler;
 
use Symfony\Component\Scheduler\Attribute\AsCronTask;
 
#[AsCronTask('0 3 * * *')]
class CleanupOldSessions
{
    public function __construct(private SessionRepository $sessions) {}
 
    public function __invoke(): void
    {
        $this->sessions->deleteOlderThan(new \DateTimeImmutable('-30 days'));
    }
}

Класс выполняется каждый день в 3:00. Для cron-выражений нужен пакет dragonmantank/cron-expression.

Формат выражения

Пять полей: минута, час, день месяца, месяц, день недели.

ВыражениеКогда
* * * * *Каждую минуту
*/5 * * * *Каждые пять минут
0 * * * *В начале каждого часа
0 3 * * *Каждый день в 3:00
0 9 * * 1По понедельникам в 9:00
0 0 1 * *Первого числа каждого месяца

Задача через равные промежутки

use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
 
#[AsPeriodicTask(frequency: '10 minutes')]
class CheckPendingPayments
{
    public function __invoke(): void
    {
        // проверка оплат
    }
}

Команда по расписанию

Тем же атрибутом можно пометить консольную команду:

#[AsCommand(name: 'app:send-report')]
#[AsCronTask('0 9 * * 1')]
class SendReportCommand extends Command
{
    // …
}

Запуск

Расписание выполняет обработчик Messenger:

php bin/console messenger:consume scheduler_default

На сервере его держат запущенным под управлением Supervisor или systemd — как и обработчик очереди.

Просмотр расписания

php bin/console debug:scheduler

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

Свой график

Когда расписание зависит от настроек или данных, его собирают в классе:

use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
 
#[AsSchedule('reports')]
class ReportSchedule implements ScheduleProviderInterface
{
    public function getSchedule(): Schedule
    {
        return (new Schedule())
            ->add(RecurringMessage::cron('0 9 * * 1', new SendWeeklyReport()))
            ->add(RecurringMessage::every('1 hour', new RefreshRates()));
    }
}

Такой график запускают отдельно: messenger:consume scheduler_reports.

Частая ошибка

Если запустить два обработчика одного расписания — например, на двух серверах, — каждая задача выполнится дважды: отчёт уйдёт два раза, оплата проверится параллельно. Держите один обработчик на график или подключите блокировку методом lock() у Schedule — тогда задачу возьмёт только один процесс.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.