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

Digital-студия WNDER

Зачем PHP-разработчику RabbitMQ

Представьте: пользователь нажимает «Заказать», а сайт должен отправить письмо, сгенерировать PDF-счёт и обновить склад. Если делать всё сразу, страница будет грузиться вечность. RabbitMQ решает проблему: PHP-скрипт быстро кладёт задачу в очередь и отвечает пользователю, а отдельный процесс-воркер спокойно разгребает задачи.

RabbitMQ — это не просто «очередь». Это полноценный брокер с обменниками (exchanges), очередями (queues) и привязками (bindings). Он умеет маршрутизировать сообщения по правилам, гарантировать доставку и даже хранить сообщения на диске, если что-то упадёт.

В PHP для работы с RabbitMQ есть библиотека php-amqplib. Она предоставляет удобный API и не требует установки нативных расширений (хотя есть и расширение amqp, но мы возьмём более популярный вариант).

Установка и первое подключение

Сначала установите RabbitMQ. Проще всего через Docker:

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

Порт 5672 — для AMQP, 15672 — для веб-интерфейса (логин/пароль: guest/guest).

Теперь установим PHP-библиотеку через Composer:

composer require php-amqplib/php-amqplib

Создадим файл send.php, который отправит сообщение в очередь:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// Подключаемся к RabbitMQ
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// Объявляем очередь (если её нет, она создастся)
$channel->queue_declare('email_queue', false, false, false, false);

// Формируем сообщение
$data = json_encode(['email' => 'user@example.com', 'subject' => 'Привет']);
$msg = new AMQPMessage($data, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);

// Публикуем в очередь
$channel->basic_publish($msg, '', 'email_queue');

echo "Сообщение отправлено\n";

// Закрываем соединение
$channel->close();
$connection->close();

Запустите скрипт — в веб-интерфейсе RabbitMQ (http://localhost:15672) увидите новую очередь с одним сообщением.

Как принимать сообщения: воркер на PHP

Теперь напишем скрипт-потребитель (worker.php), который будет забирать сообщения из очереди и обрабатывать их. Обычно такой скрипт запускают как демон или через supervisor.

<?php
require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('email_queue', false, false, false, false);

echo " [*] Ожидание сообщений. Для выхода нажмите CTRL+C\n";

$callback = function ($msg) {
    $data = json_decode($msg->body, true);
    echo "Получено: " . print_r($data, true) . "\n";
    // Здесь можно отправить письмо, сгенерировать PDF и т.д.
    // Подтверждаем обработку
    $msg->ack();
};

$channel->basic_consume('email_queue', '', false, false, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

$channel->close();
$connection->close();

Запустите воркер в терминале: php worker.php. Он будет висеть и ждать сообщений. Как только вы запустите send.php, воркер выведет данные и подтвердит обработку.

Правило: Всегда подтверждайте обработку сообщения ($msg->ack()) только после успешного выполнения задачи. Если воркер упадёт до подтверждения, RabbitMQ вернёт сообщение в очередь и его получит другой воркер. Это гарантирует, что задача не потеряется.

Обменники и маршрутизация: не только очереди

В примерах выше мы публиковали сообщение напрямую в очередь (используя пустой обменник). Но RabbitMQ славится гибкой маршрутизацией через обменники (exchanges). Самые популярные типы:

  • direct — сообщение попадает в очередь с точным совпадением ключа маршрутизации.
  • fanout — рассылка во все привязанные очереди (как broadcast).
  • topic — маршрутизация по шаблону (например, user.*).
  • headers — по заголовкам сообщения (реже используется).

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

// Создаём обменник
$channel->exchange_declare('logs', 'fanout', false, false, false);

// Публикуем в обменник (ключ маршрутизации пустой)
$channel->basic_publish($msg, 'logs', '');

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

$channel->queue_declare('log_queue_1', false, false, false, false);
$channel->queue_bind('log_queue_1', 'logs', '');

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

В реальной жизни воркеры падают, сеть моргает, а сообщения бывают «битыми». Чтобы не потерять данные, используйте механизм подтверждений (ack) и настройку basic_qos для ограничения количества неподтверждённых сообщений на воркер.

Если сообщение не удалось обработать (например, некорректный JSON), его можно отклонить (basic_nack) с параметром requeue=false, чтобы оно не крутилось в очереди бесконечно. Такие сообщения обычно отправляют в «очередь мёртвых писем» (dead letter queue).

Пример настройки очереди с DLX (Dead Letter Exchange):

$args = new \PhpAmqpLib\Wire\AMQPTable([
    'x-dead-letter-exchange' => 'dlx_exchange',
]);
$channel->queue_declare('main_queue', false, false, false, false, false, $args);

Теперь, если сообщение будет отклонено или истечёт его TTL, оно автоматически попадёт в обменник dlx_exchange, откуда его можно перенаправить в отдельную очередь для разбора.

Лайфхак: Установите плагин rabbitmq_management (он уже включён в Docker-образ с тегом management). Через веб-интерфейс удобно смотреть очереди, отправлять тестовые сообщения и наблюдать за графиками. А для продакшена настройте мониторинг через Prometheus или Zabbix.

Сравнение способов работы с RabbitMQ в PHP

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

Инструмент Плюсы Минусы
php-amqplib Чистый PHP, легко ставить, гибкий API Медленнее нативного расширения, требует больше памяти
ext-amqp Высокая производительность, низкое потребление памяти Требует установки PECL-расширения, меньше примеров
Gearman (альтернатива) Проще в настройке для простых задач Нет такой гибкой маршрутизации, меньше возможностей

Для большинства проектов на PHP стартовать с php-amqplib — правильное решение. Если упрётесь в производительность, можно перейти на расширение amqp.

Что в итоге

RabbitMQ — мощный инструмент для асинхронной обработки задач в PHP. Он помогает разгрузить сайт, сделать систему отказоустойчивой и легко масштабировать воркеры. Главное — не забывайте про подтверждения (ack), настройку dead letter exchange и мониторинг. Начните с простого примера из статьи, а затем постепенно усложняйте архитектуру.

Если вы только знакомитесь с очередями, попробуйте для начала вынести отправку писем в отдельный воркер. Это сразу даст ощутимый прирост скорости ответа сайта.

Студия WNDER