Что такое Headers Exchange в RabbitMQ?

«Что такое Headers Exchange в RabbitMQ?» — вопрос из категории Брокеры сообщений, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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 предпочтительнее.