Сервис уведомлений нашего магазина должен рассылать письма: «заказ оплачен», «товар отправлен». Писем много, отправляются они медленно, а терять их нельзя. В прошлом уроке мы выяснили, что для такой работы нужен брокер сообщений. Сегодня поднимем RabbitMQ, напишем на Node.js первого отправителя (producer) и получателя (consumer) и посмотрим, как брокер не теряет сообщения, когда воркер падает.
Что такое RabbitMQ
RabbitMQ — один из самых популярных брокеров сообщений с открытым кодом. Он написан на Erlang, работает как отдельный сервер и общается с приложениями по протоколу AMQP 0-9-1 (поддерживает и другие, например MQTT). Клиентские библиотеки есть для всех популярных языков, поэтому сервисы на Node.js, Python и Java обмениваются сообщениями через один брокер. В Node.js мы будем использовать библиотеку amqplib.
Модель AMQP: пять деталей
Вспомните почту: вы опускаете письмо в ящик, сортировочный центр решает, куда его отправить, а почтальон доставляет адресату. В AMQP устройство такое же:
- Producer — приложение, которое публикует сообщения.
- Exchange — «сортировочный центр»: принимает сообщение и решает, в какие очереди его положить. Сам сообщения не хранит.
- Binding — правило «exchange → очередь» с ключом привязки (binding key).
- Queue — очередь: хранит сообщения, пока их не заберут.
- Consumer — приложение, которое забирает сообщения из очереди и обрабатывает.
Важно: producer никогда не пишет в очередь напрямую, только в exchange. Но у RabbitMQ есть безымянный exchange по умолчанию (default exchange, имя — пустая строка): каждая очередь автоматически привязана к нему под своим именем. Поэтому channel.sendToQueue('emails', body) — сокращение для channel.publish('', 'emails', body). Остальные типы exchange разберём в следующем уроке.
Запускаем RabbitMQ в Docker
Вы уже запускали PostgreSQL и Redis через Docker Compose, RabbitMQ запускается так же. Нужен образ с суффиксом management: в него встроен веб-интерфейс.
services:
rabbitmq:
image: rabbitmq:4-management
hostname: rabbitmq
ports:
- "5672:5672" # AMQP: applications connect here
- "15672:15672" # management UI in the browser
volumes:
- rabbitmq-data:/var/lib/rabbitmq
volumes:
rabbitmq-data:Порт 5672 нужен приложениям (AMQP), порт 15672 — веб-интерфейсу. Параметр hostname задан не случайно: RabbitMQ хранит данные под именем узла, которое берёт из имени хоста, и без фиксированного имени после пересоздания контейнера прежние данные не найдутся. Запускаем брокер и ставим клиентскую библиотеку:
docker compose up -d
npm install amqplib