Что такое маршрутизация сообщений в RabbitMQ и как она работает?

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

Ответ

Маршрутизация сообщений в RabbitMQ — это механизм, с помощью которого обменник (exchange) решает, в какие очереди (queues) отправить поступившее сообщение. Логика маршрутизации определяется типом обменника и привязками (bindings) между обменником и очередями.

Основные типы обменников и их логика маршрутизации:

  1. Direct Exchange (Прямой)

    • Принцип: Сообщение отправляется в очереди, чей ключ маршрутизации (routing key) полностью совпадает с routing key сообщения.
    • Использование: Точечная отправка, задачи по типу (например, logs.error, orders.process).
      // Объявление обменника
      channel.ExchangeDeclare(exchange: "direct_logs", type: ExchangeType.Direct);
      // Привязка очереди с ключом "error"
      channel.QueueBind(queue: "queue_errors", exchange: "direct_logs", routingKey: "error");
      // Публикация. Сообщение попадёт ТОЛЬКО в очередь "queue_errors"
      channel.BasicPublish(exchange: "direct_logs", routingKey: "error", basicProperties: null, body: messageBody);
  2. Fanout Exchange (Широковещательный)

    • Принцип: Сообщение копируется и отправляется во ВСЕ привязанные к обменнику очереди. Routing key игнорируется.
    • Использование: Широковещательные уведомления, обновления кэша для всех инстансов приложения.
      channel.ExchangeDeclare(exchange: "fanout_updates", type: ExchangeType.Fanout);
      // Сообщение будет отправлено во все привязанные очереди (queue_a, queue_b, ...)
      channel.BasicPublish(exchange: "fanout_updates", routingKey: "", basicProperties: null, body: messageBody);
  3. Topic Exchange (Топический)

    • Принцип: Маршрутизация по шаблону. Routing key сообщения (например, "quick.orange.rabbit") сопоставляется с шаблоном ключа в привязке, который может содержать подстановочные знаки:
      • * (звездочка) — заменяет ровно одно слово.
      • # (решетка) — заменяет ноль или более слов.
    • Использование: Сложные системы подписок, фильтрация событий по категориям.
      channel.ExchangeDeclare(exchange: "topic_logs", type: ExchangeType.Topic);
      channel.QueueBind(queue: "queue_all_errors", exchange: "topic_logs", routingKey: "*.error");
      channel.QueueBind(queue: "queue_critical", exchange: "topic_logs", routingKey: "critical.#");
      // Сообщение с ключом "app.error" попадёт в queue_all_errors
      // Сообщение с ключом "critical.system.database" попадёт в queue_critical
  4. Headers Exchange (Заголовочный)

    • Принцип: Маршрутизация происходит на основе заголовков сообщения (headers), а не routing key. Привязка задаёт условия сопоставления (x-match: all — все заголовки должны совпасть, x-match: any — достаточно любого).

Важное замечание: Обменники не хранят сообщения. Если на момент публикации сообщения нет ни одной очереди, удовлетворяющей правилам маршрутизации, сообщение будет безвозвратно утеряно, если не установлен флаг mandatory. Для обработки таких случаев используется механизм возвратов (Returned Messages).