Ответ
Headers Exchange — это особый тип обменника (exchange) в RabbitMQ, который маршрутизирует сообщения не по ключу маршрутизации (routing key), а на основе заголовков (headers) — пары ключ-значение в свойствах сообщения. Это позволяет реализовать сложную логику маршрутизации, основанную на нескольких атрибутах.
Сравнение с другими типами exchange:
| Тип Exchange | Основа маршрутизации | Пример использования |
|---|---|---|
| Direct | Точное совпадение routing key | Распределение задач по типам (task.image, task.pdf) |
| Topic | Шаблон routing key (*, #) |
События по иерархии (logs.app.error, user.registered) |
| Fanout | Без маршрутизации (все очереди) | Широковещательные уведомления |
| Headers | Совпадение заголовков сообщения | Фильтрация по атрибутам (format=json, version=2, priority=high) |
Ключевой параметр: x-match
При привязке очереди к Headers Exchange указывается аргумент x-match, который определяет логику сопоставления:
x-match = all(по умолчанию): Все пары ключ-значение в аргументах привязки должны присутствовать и совпадать со заголовками сообщения (логическое И).x-match = any: Достаточно совпадения хотя бы одной пары ключ-значение (логическое ИЛИ).
Пример на C# (с использованием библиотеки RabbitMQ.Client):
using RabbitMQ.Client;
using System.Text;
using System.Text.Json;
// 1. Создание соединения и канала
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// 2. Объявление Headers Exchange
channel.ExchangeDeclare(exchange: "notifications_headers", type: ExchangeType.Headers, durable: true);
// 3. Объявление очередей и привязка с разными условиями
// Очередь для JSON-уведомлений высокой важности
channel.QueueDeclare("queue_critical_json", durable: true, exclusive: false, autoDelete: false);
var criticalBindArgs = new Dictionary<string, object>
{
{ "format", "json" },
{ "priority", "high" },
{ "x-match", "all" } // ДОЛЖНЫ быть ОБА заголовка: format=json И priority=high
};
channel.QueueBind(queue: "queue_critical_json", exchange: "notifications_headers", routingKey: "", arguments: criticalBindArgs);
// Очередь для любых XML-уведомлений ИЛИ уведомлений с тегом "legacy"
channel.QueueDeclare("queue_general", durable: true, exclusive: false, autoDelete: false);
var generalBindArgs = new Dictionary<string, object>
{
{ "format", "xml" },
{ "tag", "legacy" },
{ "x-match", "any" } // Достаточно format=xml ИЛИ tag=legacy
};
channel.QueueBind(queue: "queue_general", exchange: "notifications_headers", routingKey: "", arguments: generalBindArgs);
// 4. Публикация сообщения с заголовками
var message = new { Text = "Важное событие!" };
var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message));
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
properties.Headers = new Dictionary<string, object>
{
{ "format", "json" },
{ "priority", "high" },
{ "source", "api" }
};
// Сообщение будет доставлено ТОЛЬКО в очередь "queue_critical_json",
// так как совпали ВСЕ требуемые заголовки (format=json, priority=high).
// В "queue_general" оно не попадет, так как нет ни format=xml, ни tag=legacy.
channel.BasicPublish(exchange: "notifications_headers",
routingKey: "", // Для Headers Exchange routing key игнорируется или может быть пустым
basicProperties: properties,
body: body);
Когда использовать Headers Exchange?
- Сложная фильтрация: Когда для маршрутизации нужны несколько независимых критериев (например,
версия API=2,регион=EU,тип клиента=premium). - Маршрутизация по атрибутам, а не по теме: Если логика не укладывается в иерархическую структуру Topic Exchange.
- Динамическая подписка: Клиенты могут гибко настраивать, сообщения с каким набором атрибутов они хотят получать.
Недостаток:
Маршрутизация по заголовкам менее эффективна, чем по routing key, так как требует сопоставления хэш-таблиц. Для высоконагруженных систем с простой логикой Direct или Topic Exchange предпочтительнее.
Ответ 18+ 🔞
Смотри, Headers Exchange в RabbitMQ — это как самый занудный и придирчивый начальник на районе. Он вообще не смотрит на routing key, который все остальные используют. Ему подавай заголовки, эти пары ключ-значение в свойствах сообщения. Хочешь сложную логику, где надо учесть кучу условий сразу? Вот он твой выбор, ёпта.
Чем он не такой, как все:
| Тип Exchange | На что смотрит | Для чего годится |
|---|---|---|
| Direct | Точное совпадение ключа маршрутизации | Разделить задачи: вот тебе картинки (task.image), а вот тебе PDF (task.pdf). |
| Topic | Шаблон ключа (звёздочки, решётки) | События по дереву: logs.app.error, user.registered. |
| Fanout | Ни на что не смотрит, шлёт всем | Просто орать на всю площадь, чтобы все услышали. |
| Headers | Совпадение заголовков сообщения | Вот тут уже интересно: format=json, version=2, priority=high. Фильтрует по атрибутам. |
Главная фишка: x-match
Когда привязываешь очередь к такому обменнику, ты должен указать x-match. Это как сказать ему: «Слушай, а как ты будешь сравнивать?»
x-match = all(стоит по умолчанию): Все указанные тобой условия должны совпасть. Это логическое «И». Жёстко, зато точно.x-match = any: Достаточно совпадения хотя бы по одному пункту. Логическое «ИЛИ». Попроще, поживее.
Пример на C#, чтобы было понятно, о чём речь:
using RabbitMQ.Client;
using System.Text;
using System.Text.Json;
// Подключаемся, как обычно
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// Объявляем самого придирчивого обменника
channel.ExchangeDeclare(exchange: "notifications_headers", type: ExchangeType.Headers, durable: true);
// Теперь создаём очереди и говорим им, на какие заголовки подписываться
// Очередь для ВАЖНЫХ JSON-уведомлений. Будет оч-чень привередливой.
channel.QueueDeclare("queue_critical_json", durable: true, exclusive: false, autoDelete: false);
var criticalBindArgs = new Dictionary<string, object>
{
{ "format", "json" },
{ "priority", "high" },
{ "x-match", "all" } // Важно! Должны совпасть ОБА условия: И format=json, И priority=high.
};
channel.QueueBind(queue: "queue_critical_json", exchange: "notifications_headers", routingKey: "", arguments: criticalBindArgs);
// Очередь для общих уведомлений. Попроще, жадная до всего.
channel.QueueDeclare("queue_general", durable: true, exclusive: false, autoDelete: false);
var generalBindArgs = new Dictionary<string, object>
{
{ "format", "xml" },
{ "tag", "legacy" },
{ "x-match", "any" } // А тут достаточно ЛЮБОГО из условий: format=xml ИЛИ tag=legacy.
};
channel.QueueBind(queue: "queue_general", exchange: "notifications_headers", routingKey: "", arguments: generalBindArgs);
// А теперь публикуем сообщение
var message = new { Text = "Важное событие!" };
var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message));
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
// Вот наши волшебные заголовки
properties.Headers = new Dictionary<string, object>
{
{ "format", "json" },
{ "priority", "high" },
{ "source", "api" }
};
// Что произойдёт?
// Сообщение полетит ТОЛЬКО в "queue_critical_json", потому что у него format=json И priority=high. Оба условия сошлись!
// В "queue_general" оно не попадёт, потому что у него нет ни format=xml, ни tag=legacy. Не повезло.
channel.BasicPublish(exchange: "notifications_headers",
routingKey: "", // Ключ маршрутизации тут вообще не важен, можно пустой
basicProperties: properties,
body: body);
Когда его использовать, а когда нет?
Используй, когда:
- Логика фильтрации — просто пиздец сложная. Нужно учесть кучу независимых параметров:
версия API=2,регион=EU,тип клиента=premium. - Маршрутизация идёт по свойствам, а не по теме. Не лезет в иерархию Topic Exchange.
- Клиенты хотят гибко настраивать подписку. «Дайте мне всё, где
format=jsonИpriority=high, но если естьtag=test, то нахуй не надо».
Не используй, когда:
- Нужна просто скорость. Маршрутизация по заголовкам — это сравнение хэш-таблиц, оно тяжелее, чем просто матчинг по строке ключа. Для простых и быстрых сценариев бери Direct или Topic.
- Логика простая как три копейки. Не надо тащить сюда слона, если можно обойтись мухой.
Короче, Headers Exchange — это мощный инструмент для сложных случаев, но за его гибкость платишь производительностью. Как говорится, понимаешь, за что боролся.