Ответ
Exchange в RabbitMQ — это сущность (виртуальный почтовый узел), которая получает сообщения от продюсеров и маршрутизирует их в одну или несколько очередей. Продюсер никогда не отправляет сообщение напрямую в очередь, он публикует его в Exchange, указывая ключ маршрутизации (routing key).
Типы Exchange и их логика маршрутизации
| Тип | Логика маршрутизации | Аналогия |
|---|---|---|
| Direct | Сообщение попадает в очередь, чей binding key точно совпадает с routing key сообщения. |
Письмо с конкретным адресом. |
| Fanout | Сообщение копируется во все очереди, привязанные к данному Exchange. Игнорирует routing key. |
Радиовещание. |
| Topic | Сообщение попадает в очередь, чей binding key соответствует шаблону routing key. Шаблоны используют символы * (одно слово) и # (ноль или несколько слов). |
Подписка на темы (например, logs.error.*). |
| Headers | Маршрутизация основана на заголовках сообщения, а не на routing key. Привязка указывает условия сопоставления. |
Фильтрация по атрибутам. |
Пример: Объявление и использование Topic Exchange на C
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// 1. Объявляем Topic Exchange
channel.ExchangeDeclare(exchange: "notifications_topic",
type: ExchangeType.Topic,
durable: true);
// 2. Объявляем очереди и привязываем их с разными binding keys
channel.QueueDeclare(queue: "email_queue");
channel.QueueBind(queue: "email_queue",
exchange: "notifications_topic",
routingKey: "notifications.email.#"); // Все email-уведомления
channel.QueueDeclare(queue: "critical_queue");
channel.QueueBind(queue: "critical_queue",
exchange: "notifications_topic",
routingKey: "#.critical"); // Все критические уведомления
// 3. Публикуем сообщение с routing key
var body = Encoding.UTF8.GetBytes("Server is down!");
// Это сообщение попадет в ОБЕ очереди, т.к. ключ "notifications.email.critical"
// соответствует и "notifications.email.#", и "#.critical"
channel.BasicPublish(exchange: "notifications_topic",
routingKey: "notifications.email.critical",
basicProperties: null,
body: body);
Важное правило
Exchange не хранит сообщения. Если опубликованное сообщение не может быть маршрутизировано ни в одну очередь (нет подходящих привязок), оно будет безвозвратно потеряно, если не объявлен Alternate Exchange для обработки таких "сирот".
Ответ 18+ 🔞
А, слушай, про RabbitMQ и его Exchange'и — это вообще отдельная песня, блядь. Представь себе не почту, а какую-то ебанутую сортировочную станцию в голове у долбоёба. Продюсер — это ты, который принёс посылку. Ты её не суёшь прямо в ящик получателю (очередь), ты тащишь её на эту станцию (Exchange) и орёшь: «На, маршрутизируй это, сука, по такому-то ключу!». А дальше уже эта контора сама решает, в какие ящики эту хуйню пихать.
Вот какие бывают эти сортировочные конторы, ёпта:
| Тип | Как работает | На что похоже |
|---|---|---|
| Direct | Сообщение попадёт только в ту очередь, чей binding key полностью совпал с routing key от сообщения. Точно в цель. |
Как письмо с точным адресом квартиры. Не ошибёшься. |
| Fanout | Сообщение тупо копируется во все очереди, которые к этой станции привязаны. Ключ маршрутизации всем похуй. | Как радиостанция — кто включил, тот и слышит. |
| Topic | Сообщение идёт в очередь, чей binding key подходит под шаблон ключа сообщения. Там можно звездочки * (одно слово) и решётки # (ноль или много слов) использовать. |
Как подписка на теги в телеге. Хочешь logs.error.* — получишь. |
| Headers | Маршрутизация по заголовкам сообщения, а не по ключу. Привязка — это набор условий, которые надо проверить. | Как фильтр в интернет-магазине: «Покажи всё красное и размера XL». |
И главное, что все забывают, блядь: Exchange сообщения не хранит, сука, вообще. Это просто сортировщик-распределитель. Если твою посылку некуда деть — нет ни одной привязанной очереди, которая подходит по правилам, — то её выкинут нахуй в никуда. Насовсем. Чтобы такого не было, надо заранее «Alternate Exchange» настраивать, куда такие сиротки будут складывать. Но это уже для параноиков.
Смотри, как на C# с Topic Exchange развлекаются:
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// 1. Объявляем Topic Exchange
channel.ExchangeDeclare(exchange: "notifications_topic",
type: ExchangeType.Topic,
durable: true);
// 2. Объявляем очереди и привязываем их с разными binding keys
channel.QueueDeclare(queue: "email_queue");
channel.QueueBind(queue: "email_queue",
exchange: "notifications_topic",
routingKey: "notifications.email.#"); // Все email-уведомления
channel.QueueDeclare(queue: "critical_queue");
channel.QueueBind(queue: "critical_queue",
exchange: "notifications_topic",
routingKey: "#.critical"); // Все критические уведомления
// 3. Публикуем сообщение с routing key
var body = Encoding.UTF8.GetBytes("Server is down!");
// Это сообщение попадет в ОБЕ очереди, т.к. ключ "notifications.email.critical"
// соответствует и "notifications.email.#", и "#.critical"
channel.BasicPublish(exchange: "notifications_topic",
routingKey: "notifications.email.critical",
basicProperties: null,
body: body);
Вот видишь? Отправил одно сообщение с ключом notifications.email.critical, а оно, хитрая жопа, улетело сразу в две очереди. Потому что шаблоны сошлись. Красота, блядь. Главное — не отправляй сообщения в Exchange, к которому ничего не привязано, а то они просто испарятся, и потом будешь ебаться, искать, куда делось.