Расскажи про свой опыт работы с RabbitMQ

Ответ

Я использовал RabbitMQ в нескольких проектах на PHP (Symfony/Laravel) для построения асинхронной, отказоустойчивой коммуникации между сервисами.

Основные сценарии:

  • Фоновая обработка задач: отправка email, генерация PDF-отчетов, обработка загруженных файлов. Задачи помещались в очередь, а воркеры (обычно Symfony Console-команды или Laravel Queues с драйвером rabbitmq) обрабатывали их независимо от основного HTTP-запроса.
  • Событийная архитектура (Event-Driven): при изменении сущности в одном микросервисе (например, «Заказ создан») публиковалось событие в RabbitMQ. Другие сервисы (сервис уведомлений, сервис аналитики) подписывались на это событие и реагировали, не создавая прямых жестких зависимостей.
  • Балансировка нагрузки: несколько экземпляров воркеров подписывались на одну очередь, и RabbitMQ распределял сообщения между ними по принципу round-robin.

С чем работал и пример кода (PHP с библиотекой php-amqplib):

// Публикация сообщения с персистентностью
$connection = new AMQPStreamConnection('rabbitmq-host', 5672, 'user', 'pass');
$channel = $connection->channel();

// Объявление очереди (durable=true для сохранения при перезагрузке брокера)
$channel->queue_declare('order_processing', false, true, false, false);

$messageBody = json_encode(['order_id' => 123, 'action' => 'created']);
$msg = new AMQPMessage($messageBody, [
    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
]);

$channel->basic_publish($msg, '', 'order_processing');
$channel->close();
$connection->close();

Продвинутые практики:

  • Dead Letter Exchanges (DLX): Настраивал для перемещения «отравленных» сообщений (которые не удалось обработать после N попыток) в отдельную очередь для анализа.
  • TTL (Time-To-Live): Использовал для сообщений с ограниченным сроком актуальности (например, коды подтверждения).
  • Подтверждения (Acknowledgements): Всегда включал $channel->basic_ack() в воркерах после успешной обработки, чтобы избежать потери сообщений.
  • Кластеризация: На production-окружении разворачивал кластер RabbitMQ (как минимум два узла) с зеркалированием очередей для обеспечения высокой доступности.

Ответ 18+ 🔞

Слушай, я с этой кроличьей фермой, RabbitMQ, на PHP так намаялся, что можно книгу писать. В основном на Symfony и Laravel крутил, когда нужно было, чтобы сервисы между собой болтали, но не синхронно, по-быстрому, а чтоб надёжно и чтобы всё не рухнуло, если один из них вдруг накрылся медным тазом.

Где это говно пригождалось, ёпта:

  • Фоновые задачи, которые всех заёбывают: Ну там письма рассылать, отчёты генерировать — овердохуища работы. Вместо того чтобы юзеру полчаса ждать, пока PDF выплюнет, ты просто кидаешь задачу в очередь и говоришь «разбирайтесь». А воркеры на другом конце, как голодные псы, их и хавают. В Laravel это через их Queues делается, только драйвер rabbitmq ставишь, а в Symfony — консольные команды, которые слушают канал.
  • Архитектура на событиях, чтобы сервисы не знали друг о друге в лицо: Допустим, в сервисе заказов создали новый заказ. Вместо того чтобы тупо дергать API сервиса нотификаций и сервиса аналитики (а если они легли — пиши пропало), мы просто орем в RabbitMQ: «Эй, народ, заказ-то создался!». А кто хочет — тот подписывается и делает что надо. Никаких прямых связей — красота, блядь.
  • Балансировка, чтоб не перегрузить одного: Можно запустить десять одинаковых воркеров, подписать их на одну очередь, и кролик сам будет раскидывать сообщения по ним по кругу. Один воркер упал — да похуй, остальные работают. Просто и гениально.

Вот на что руки набил и кусок кода для наглядности (PHP с php-amqplib):

// Отправка сообщения, чтоб оно даже после перезагрузки брокера не потерялось
$connection = new AMQPStreamConnection('rabbitmq-host', 5672, 'user', 'pass');
$channel = $connection->channel();

// Объявляем очередь (durable=true — это чтобы она пережила ребут кролика)
$channel->queue_declare('order_processing', false, true, false, false);

$messageBody = json_encode(['order_id' => 123, 'action' => 'created']);
$msg = new AMQPMessage($messageBody, [
    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT // Вот эта штука — самое важное
]);

$channel->basic_publish($msg, '', 'order_processing');
$channel->close();
$connection->close();

А вот где начинается настоящая магия, когда уже мозг включать надо:

  • Dead Letter Exchanges (DLX): Это, блядь, спасение. Если сообщение никак не обрабатывается (воркер его пять раз прочитал и выплюнул), то вместо того чтобы болтаться в очереди вечно, оно летит в специальную «мертвую» очередь. Потом открываешь, смотришь — ага, сообщение-то кривое, данные не те. Без этого — терпения ноль ебать, искать ошибку.
  • TTL (Время жизни): Ставишь сообщению срок годности. Идеально для всяких одноразовых кодов подтверждения. Прошло 5 минут — сообщение самоуничтожилось, и воркеру даже не пришло.
  • Подтверждения (Acknowledgements): Это святое. Воркер должен явно сказать кролику: «Окей, чувак, я сообщение проглотил и переварил, можешь его удалять». Если не сделать — при перезапуске воркера сообщения потекут рекой обратно, и будет пиздец, дубликаты по всей системе.
  • Кластеризация в продакшене: Один кролик — это точка отказа, это пизда рулю. Поэтому ставил кластер из двух-трёх нод с зеркалированием очередей. Одна нода легла — остальные продолжают работать. Без этого на production выходить — это чисто идиотизм, ядрёна вошь.