Ответ
Поведение зависит от флага mandatory, который указывает, что делать с сообщением, если его невозможно доставить в очередь.
1. mandatory: false (значение по умолчанию)
Сообщение будет безвозвратно отброшено брокером. Отправитель не получит никакого уведомления об ошибке. Это поведение называется "fire and forget" и подходит для сценариев, где потеря отдельных сообщений допустима.
2. mandatory: true
Если exchange не существует, брокер вернет сообщение отправителю через механизм Basic.Return. Отправитель должен быть готов обработать это событие.
Практический пример с обработкой возврата:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// 1. Подписываемся на событие возврата сообщения
channel.BasicReturn += (sender, args) =>
{
var body = args.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"[RETURN] Сообщение возвращено!");
Console.WriteLine($" Причина: {args.ReplyText}");
Console.WriteLine($" Exchange: {args.Exchange}");
Console.WriteLine($" Routing Key: {args.RoutingKey}");
Console.WriteLine($" Тело: {message}");
};
// 2. Публикуем сообщение в несуществующий exchange с mandatory=true
var body = Encoding.UTF8.GetBytes("Test Message");
var properties = channel.CreateBasicProperties();
properties.Persistent = true; // Делаем сообщение устойчивым
channel.BasicPublish(
exchange: "non_existent_exchange", // Несуществующий exchange
routingKey: "some.routing.key",
mandatory: true, // Ключевой параметр
basicProperties: properties,
body: body);
Console.WriteLine("Сообщение опубликовано. Ожидайте Basic.Return...");
Вывод в консоли будет примерно таким:
Сообщение опубликовано. Ожидайте Basic.Return...
[RETURN] Сообщение возвращено!
Причина: NO_ROUTE
Exchange: non_existent_exchange
Routing Key: some.routing.key
Тело: Test Message
Рекомендации:
- Всегда используйте
mandatory: trueв системах, где критична гарантированная доставка или необходима мгновенная реакция на ошибки маршрутизации. - Для повышения надежности комбинируйте это с подтверждениями публикации (Publisher Confirms) и устойчивыми сообщениями (
Persistent = true). - В продакшене обязательно реализуйте логику повторной отправки или перемещения "отравленных" сообщений в Dead Letter Exchange (DLX).
Ответ 18+ 🔞
Давай разберем эту штуку с флагом mandatory, а то народ часто путается, как оно работает на самом деле. Представь, что ты шлёшь посылку через какую-то контору.
1. mandatory: false (дефолтное состояние, все так живут)
Это как отправить посылку и тут же забыть. Ты кинул её в ящик этой конторы и пошёл по своим делам. Если адресат (очередь) не найден или сам пункт приёма (exchange) сгорел — ну и хуй с ним. Посылка тупо пропадёт, а тебе никто даже не позвонит, чтобы сказать "чувак, у нас тут проблемка". Подходит, когда потеря пары сообщений — не конец света. Типа "отправил и забыл", fire and forget, как говорят умные дядьки.
2. mandatory: true (включил режим параноика)
А вот это уже серьёзнее. Ты говоришь брокеру: "Слушай, пацан, если не сможешь доставить эту хуйню куда надо — немедленно верни её мне обратно, я разберусь!". Если exchange не существует или для сообщения нет подходящего маршрута, брокер не будет молча его выбрасывать. Вместо этого он вернёт тебе сообщение через специальный механизм — Basic.Return. Но чтобы его получить, ты должен заранее подписаться на эту фигню, иначе возврат улетит в никуда.
Пример на пальцах, как это выглядит в коде:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
// 1. Вешаем слушателя на возвраты. Без этого — нихуя не получишь!
channel.BasicReturn += (sender, args) =>
{
var body = args.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"[ОЙ, ВСЁ!] Сообщение вернулось, как бумеранг!");
Console.WriteLine($" Причина провала: {args.ReplyText}");
Console.WriteLine($" Exchange, который не нашёлся: {args.Exchange}");
Console.WriteLine($" Ключ маршрутизации: {args.RoutingKey}");
Console.WriteLine($" Само сообщение: {message}");
};
// 2. Пытаемся отправить сообщение в несуществующий exchange. Ну, чисто проверить систему.
var body = Encoding.UTF8.GetBytes("Тестовое сообщение, приём!");
var properties = channel.CreateBasicProperties();
properties.Persistent = true; // Делаем сообщение живучим, чтобы пережило перезагрузку
// Вот тут главный момент — mandatory: true
channel.BasicPublish(
exchange: "несуществующий_обменник", // Очевидно, его нет
routingKey: "какойто.ключ",
mandatory: true, // Включаем режим "верни, если что"
basicProperties: properties,
body: body);
Console.WriteLine("Сообщение якобы отправлено. Ждём-с...");
И что мы увидим в консоли, когда всё накроется медным тазом:
Сообщение якобы отправлено. Ждём-с...
[ОЙ, ВСЁ!] Сообщение вернулось, как бумеранг!
Причина провала: NO_ROUTE
Exchange, который не нашёлся: несуществующий_обменник
Ключ маршрутизации: какойто.ключ
Само сообщение: Тестовое сообщение, приём!
Итог и советы на жизнь:
- Ставь
mandatory: trueв системах, где каждая потерянная посылка — это пиздец, скандал и разбор полётов. Чтобы сразу знать, если маршрутизация сломалась. - Но не обольщайся — это только про маршрутизацию. Чтобы быть совсем спокойным, комбинируй это с подтверждениями от брокера (Publisher Confirms), что он вообще принял сообщение, и ставь
Persistent = true. - В реальном продакшене обязательно придумай, куда девать эти возвращённые сообщения. Обычно их пихают в Dead Letter Exchange (DLX) — специальную помойку для неудачников, чтобы потом разобраться.