Ваш сайт на Yii2 иногда подтормаживает? Пользователи жалуются на долгую отправку писем или генерацию отчётов? Скорее всего, вы выполняете тяжёлые задачи прямо в запросе. Очереди — это способ разгрузить приложение и сделать его отзывчивым. В статье разберём, как подружить Yii2 с очередями задач, какие драйверы выбрать и как не наступить на грабли.

Digital-студия WNDER

Что такое очередь задач и зачем она нужна

Очередь задач — это как список дел, которые нужно сделать потом. Вы кладёте задачу в очередь и сразу отвечаете пользователю «всё ок», а обработчик берёт задачу и выполняет её в фоне. В Yii2 для этого есть встроенный компонент queue, который поддерживает разные хранилища: от простого файла до Redis и RabbitMQ.

Зачем это нужно? Представьте, что пользователь регистрируется. Вы отправляете приветственное письмо, генерируете PDF-счёт и делаете запись в аудит. Если делать это синхронно, страница будет грузиться секунды. С очередью — миллисекунды. Пользователь доволен, сервер не перегружен.

Устанавливаем и настраиваем очередь в Yii2

Для начала установим пакет через Composer:

composer require yiisoft/yii2-queue

Теперь настроим компонент в конфиге приложения. Самый простой драйвер — файловый (подходит для разработки). В продакшене лучше использовать Redis или AMQP.

// config/console.php или config/web.php
return [
    'components' => [
        'queue' => [
            'class' => \yii\queue\drivers\File::class,
            'path' => '@runtime/queue',
        ],
    ],
];

После настройки нужно запустить обработчик. В консоли выполняем:

yii queue/listen

Эта команда будет слушать очередь и выполнять задачи по мере поступления. Для продакшена лучше использовать супервизор (например, systemd или supervisor), чтобы процесс перезапускался при падении.

Правило: никогда не запускайте обработчик очереди в том же процессе, что и веб-сервер. Это разные вещи. Веб-сервер принимает запросы, а воркер (обработчик) — выполняет задачи. Смешивать их — значит получить блокировки и странные баги.

Как создавать и отправлять задачи в очередь

Задача — это обычный класс, реализующий интерфейс \yii\queue\JobInterface. Внутри метода execute() вы пишете логику. Вот пример задачи для отправки письма:

namespace app\jobs;

use yii\base\BaseObject;
use yii\queue\JobInterface;

class SendEmailJob extends BaseObject implements JobInterface
{
    public $to;
    public $subject;
    public $body;

    public function execute($queue)
    {
        // Логика отправки письма
        Yii::$app->mailer->compose()
            ->setTo($this->to)
            ->setSubject($this->subject)
            ->setTextBody($this->body)
            ->send();
    }
}

Чтобы добавить задачу в очередь, используем компонент queue:

Yii::$app->queue->push(new SendEmailJob([
    'to' => 'user@example.com',
    'subject' => 'Добро пожаловать!',
    'body' => 'Спасибо за регистрацию.',
]));

Можно отложить выполнение на определённое время:

Yii::$app->queue->delay(300)->push(new SendEmailJob([...]));

Это выполнит задачу через 5 минут (300 секунд). Удобно для напоминаний.

Обработка ошибок и повторные попытки

Задачи могут падать: сеть отвалилась, внешний API вернул ошибку. Yii2 по умолчанию пытается выполнить задачу один раз. Но можно настроить повторные попытки. Для этого в задаче реализуем метод getTtr() (time to reserve) и переопределим поведение при ошибке.

Проще всего использовать встроенный механизм повторных попыток через настройку компонента очереди:

'queue' => [
    'class' => \yii\queue\drivers\Redis::class,
    'attempts' => 3, // количество попыток
    'ttr' => 300,    // время на выполнение в секундах
],

Если задача падает с исключением, она будет повторена до трёх раз. После этого попадёт в список неудачных (если драйвер поддерживает). Посмотреть неудачные задачи можно командой:

yii queue/info

А перезапустить все неудачные:

yii queue/retry
Лайфхак: логируйте ошибки внутри задачи. Если задача падает, вы должны знать почему. Используйте Yii::error() или пишите в отдельный лог-файл. Иначе отладка превратится в квест.

Сравнение драйверов очередей для Yii2

Выбор драйвера зависит от нагрузки и инфраструктуры. Вот таблица с популярными вариантами:

Драйвер Когда использовать Плюсы Минусы
File Разработка, малые проекты Простота, без доп. софта Не для высокой нагрузки, нет конкурентного доступа
Redis Средние и крупные проекты Быстрота, надёжность, поддержка задержек Нужен Redis-сервер
RabbitMQ (AMQP) Высокие нагрузки, сложные сценарии Гарантированная доставка, гибкая маршрутизация Сложнее в настройке
Database Небольшие проекты с уже готовой БД Не нужен отдельный сервер Медленнее, нагрузка на БД

Для большинства проектов на Yii2 оптимален Redis. Он быстрый, простой в установке и хорошо документирован. Если у вас уже есть RabbitMQ — используйте его.

Практические советы и подводные камни

  • Идемпотентность. Задача может выполниться дважды (например, при повторной попытке). Убедитесь, что повторное выполнение не навредит. Например, отправка письма дважды — плохо. Добавьте проверку: «если письмо уже отправлено, пропустить».
  • Таймауты. Устанавливайте разумное время выполнения (ttr). Если задача висит дольше, она будет прервана и возвращена в очередь. Это лучше, чем вечно висящий процесс.
  • Мониторинг. Следите за длиной очереди. Если она растёт — значит, обработчики не справляются. Добавьте больше воркеров или оптимизируйте задачи.
  • Логирование. Пишите в лог начало и конец выполнения задачи. Это поможет при отладке.
Совет: для локальной разработки используйте драйвер File и запускайте воркер в отдельном терминале. Так вы видите, как задачи выполняются в реальном времени, и не мешаете основному приложению.

Что в итоге

Очереди задач в Yii2 — это мощный инструмент, который делает приложение быстрее и надёжнее. Настройка занимает немного времени: установить пакет, сконфигурировать компонент, написать класс задачи и запустить воркер. Главное — не забывать про идемпотентность, таймауты и мониторинг. Тогда очередь станет вашим верным помощником, а не источником головной боли.

Начните с простого драйвера File, поэкспериментируйте, а когда проект вырастет — переходите на Redis или RabbitMQ. Удачи в разработке!

Студия WNDER