Ваш сайт тормозит, когда нужно отправить сотню писем или сгенерировать отчёт? Пользователи жалуются на долгую загрузку страниц? Пора познакомиться с очередями задач. Эта статья объяснит, зачем они нужны, как их готовить в PHP и какие инструменты выбрать.

Digital-студия WNDER

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

Представьте, что вы в кафе. Официант принял заказ, отнёс на кухню, а повар готовит блюда по очереди. Если бы официант сам бегал на кухню и готовил каждое блюдо, он бы не смог обслуживать новые столики. В веб-разработке очередь задач — это та самая «кухня», где тяжёлые операции выполняются асинхронно, не блокируя основной поток.

Зачем это нужно? Вот основные причины:

  • Скорость отклика. Пользователь не ждёт, пока отправится письмо или обработается видео.
  • Надёжность. Если задача упала, её можно повторить.
  • Масштабирование. Можно запустить несколько воркеров и обрабатывать задачи параллельно.

Без очереди вы рискуете получить таймауты, недовольных пользователей и головную боль при пиковых нагрузках.

Как устроена очередь: брокеры и воркеры

Очередь задач состоит из двух ключевых компонентов: брокера (хранилища задач) и воркеров (исполнителей). Брокер — это как доска с заказами, а воркеры — повара, которые эти заказы разбирают.

В PHP чаще всего используют:

  • Redis — быстрый, простой, но требует аккуратности с надёжностью.
  • RabbitMQ — мощный брокер с гарантией доставки.
  • Beanstalkd — лёгкий и специализированный.
  • Базы данных (MySQL, PostgreSQL) — подходят для простых случаев, но могут стать узким горлом.

Для примера возьмём Redis — он популярен и прост в установке. Но помните: Redis хранит данные в памяти, поэтому при падении сервера задачи могут потеряться. Для критичных операций лучше RabbitMQ.

Практика: пишем простую очередь на PHP

Давайте сделаем мини-очередь с помощью Redis и библиотеки predis/predis. Установим её через Composer:

composer require predis/predis

Теперь создадим класс для добавления задач в очередь:

<?php
require 'vendor/autoload.php';

use Predis\Client;

class Queue
{
    private $redis;
    private $queueName;

    public function __construct($queueName = 'tasks')
    {
        $this->redis = new Client([
            'scheme' => 'tcp',
            'host'   => '127.0.0.1',
            'port'   => 6379,
        ]);
        $this->queueName = $queueName;
    }

    public function push($task)
    {
        $this->redis->rpush($this->queueName, json_encode($task));
    }

    public function pop()
    {
        $task = $this->redis->lpop($this->queueName);
        return $task ? json_decode($task, true) : null;
    }
}

// Пример использования
$queue = new Queue();
$queue->push(['type' => 'email', 'to' => 'user@example.com', 'subject' => 'Привет']);

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

<?php
require 'vendor/autoload.php';

use Predis\Client;

$redis = new Client();
$queueName = 'tasks';

while (true) {
    $taskJson = $redis->lpop($queueName);
    if ($taskJson) {
        $task = json_decode($taskJson, true);
        // Обработка задачи
        if ($task['type'] === 'email') {
            // Отправка письма
            mail($task['to'], $task['subject'], 'Тело письма');
        }
        echo "Задача выполнена: " . $task['type'] . "\n";
    } else {
        sleep(1); // Нет задач — подождём
    }
}

Этот код прост, но у него есть недостатки: нет повторных попыток при ошибках, нет параллельных воркеров. Для продакшена лучше использовать готовые библиотеки, например, Laravel Queue или Symfony Messenger.

Обзор популярных инструментов для очередей в PHP

Выбор инструмента зависит от масштаба и требований. Вот сравнение основных вариантов:

Инструмент Плюсы Минусы
Redis Быстрый, простой, уже часто используется для кеша Нет гарантии доставки, данные в памяти
RabbitMQ Надёжный, гибкая маршрутизация, подтверждения Сложнее в настройке, требует отдельного сервера
Beanstalkd Лёгкий, специализированный, простой API Меньше возможностей, чем у RabbitMQ
База данных Не нужно ставить новое ПО, знакомо Медленно при больших объёмах, конкуренция за блокировки

Для старта часто берут Redis. Если проект растёт и важна надёжность — переходят на RabbitMQ.

Правило: Всегда делайте задачи идемпотентными. Это значит, что повторное выполнение задачи не должно приводить к нежелательным последствиям (например, двойному списанию денег). Используйте уникальные идентификаторы и проверяйте, не была ли задача уже выполнена.

Практические советы и лайфхаки

Вот несколько проверенных рекомендаций, которые сэкономят вам нервы:

  • Логируйте всё. Записывайте, когда задача добавлена, когда начата и завершена. Это поможет отладить.
  • Используйте таймауты. Если задача выполняется слишком долго, её нужно прервать и пометить как ошибочную.
  • Настройте мониторинг. Следите за длиной очереди. Если она растёт — значит, воркеры не справляются.
  • Разделяйте очереди. Например, для писем и для обработки изображений используйте разные очереди, чтобы тяжёлые задачи не блокировали лёгкие.
  • Не забывайте про dead letter queue. Это очередь для задач, которые не удалось выполнить после нескольких попыток. Их нужно анализировать вручную.

И ещё один важный момент: воркеры должны быть устойчивы к падениям. Используйте супервизоры (например, systemd или supervisor), чтобы автоматически перезапускать процессы.

Заключение

Очереди задач — это не роскошь, а необходимость для любого серьёзного проекта. Они позволяют сделать сайт быстрым, надёжным и масштабируемым. Начните с простого решения на Redis, а когда почувствуете потребность — переходите на более мощные инструменты. Главное — не забывайте про идемпотентность и мониторинг. Удачи!

Студия WNDER