Для чего используется RabbitMQ?

«Для чего используется RabbitMQ?» — вопрос из категории Брокеры сообщений, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

RabbitMQ — это брокер сообщений с открытым исходным кодом, реализующий протокол AMQP. В архитектуре на основе Node.js он используется как надежный "посредник" для асинхронной коммуникации между различными частями системы.

Основные сценарии использования с Node.js:

  1. Фоновая обработка задач: Выгрузка Node.js-сервера от долгих операций (отправка email, генерация PDF, обработка изображений).

    // producer.js (Express API endpoint)
    const amqp = require('amqplib');
    app.post('/tasks', async (req, res) => {
      const conn = await amqp.connect('amqp://localhost');
      const channel = await conn.createChannel();
      const queue = 'email_queue';
      await channel.assertQueue(queue, { durable: true }); // Сохраняем задачи на диске
      channel.sendToQueue(queue, Buffer.from(JSON.stringify({
        to: req.body.email,
        subject: 'Welcome'
      })), { persistent: true });
      await channel.close();
      await conn.close();
      res.send({ status: 'Task queued' }); // Ответ клиенту мгновенный
    });
  2. Интеграция микросервисов: Независимые Node.js-сервисы обмениваются событиями через RabbitMQ, не зная друг о друге напрямую (шаблон Publisher/Subscriber).

    // service-a.js (Publisher)
    channel.publish('order_events', '', Buffer.from(JSON.stringify({ event: 'OrderPlaced', id: 123 })));
    
    // service-b.js & service-c.js (Subscribers, каждый со своей очередью)
    await channel.assertExchange('order_events', 'fanout', { durable: false });
    const q = await channel.assertQueue('', { exclusive: true });
    await channel.bindQueue(q.queue, 'order_events', '');
    channel.consume(q.queue, (msg) => { /* Обработка события */ });
  3. Балансировка нагрузки между воркерами: Несколько идентичных Node.js-процессов (воркеров) потребляют задачи из одной очереди, распределяя нагрузку.

    // worker.js (Запускается в нескольких экземплярах, например, через PM2)
    channel.prefetch(1); // Обрабатываем по одной задаче за раз для честной балансировки
    channel.consume('image_processing_queue', async (msg) => {
      const task = JSON.parse(msg.content.toString());
      // ... долгая обработка ...
      console.log(`Processed task ${task.id}`);
      channel.ack(msg); // Подтверждаем успешную обработку
    });

Преимущества для Node.js-архитектуры:

  • Повышение отзывчивости API: Долгие операции выносятся в очередь.
  • Надежность: Сообщения сохраняются на диске и не теряются при перезапуске сервисов.
  • Гибкость и масштабируемость: Легко добавлять новых потребителей или воркеров.
  • Декoupling (развязка): Сервисы не зависят от доступности друг друга в момент отправки сообщения.