Отклонение сообщений

В протоколе STOMP доставка сообщения не считается завершённой в момент его получения клиентом. Завершённость определяется подтверждением обработки, которое отправляется обратно брокеру. Отсутствие подтверждения или явное отклонение приводит к повторной доставке либо перенаправлению сообщения в очередь повторных попыток или «мертвых писем» (dead-letter queue), в зависимости от конфигурации брокера.

В STOMP.js управление подтверждениями реализовано через объект сообщения и методы ack() и nack(), которые соответствуют семантике протокола STOMP 1.1/1.2.


Режимы подтверждения доставки

Поведение отклонения сообщений напрямую зависит от режима подписки, задаваемого при subscribe.

AUTO_ACK

В автоматическом режиме подтверждение отправляется брокеру сразу после доставки сообщения клиенту. Любая ошибка обработки на стороне клиента не влияет на брокер: сообщение считается успешно обработанным.

Особенности:

  • отсутствие возможности отклонения;
  • риск потери сообщений при сбоях обработки;
  • минимальные накладные расходы.

CLIENT_ACK

В режиме клиентского подтверждения ответственность за подтверждение ложится на код обработки.

Сообщение должно быть явно подтверждено:

message.ack();

Если подтверждение не отправлено, сообщение будет повторно доставлено при переподключении или истечении таймаута брокера.


CLIENT_INDIVIDUAL

Аналогичен CLIENT_ACK, но подтверждение относится строго к конкретному сообщению, а не к группе сообщений с одним subscription.

message.ack();

Поведение отклонения аналогично, но контроль более точечный и безопасный при параллельной обработке.


Отклонение сообщений (nack)

Отклонение сообщения выполняется через метод nack(), который сообщает брокеру о невозможности обработки сообщения.

message.nack();

В зависимости от реализации брокера и заголовков подписки, отклонение может приводить к разным сценариям:

  • немедленная повторная доставка;
  • помещение сообщения обратно в очередь;
  • перенаправление в dead-letter queue;
  • удаление сообщения (редко и зависит от политики брокера).

Поведение брокеров при отклонении

STOMP как протокол не фиксирует единое поведение после nack, поэтому результат определяется серверной реализацией.

RabbitMQ

При использовании STOMP-плагина RabbitMQ:

  • nack приводит к requeue по умолчанию;
  • возможно отключение requeue через аргументы подписки;
  • поддерживается DLQ через политики очередей.

ActiveMQ

ActiveMQ может:

  • возвращать сообщение в очередь;
  • увеличивать счётчик redelivery;
  • после превышения лимита направлять сообщение в DLQ.

Повторная доставка сообщений

Повторная доставка является ключевым механизмом надёжности при отклонении.

При nack или отсутствии ack:

  • сообщение остаётся неподтверждённым;
  • брокер помечает его как недоставленное;
  • при восстановлении соединения оно может быть отправлено снова.

В STOMP.js повторная доставка часто сопровождается заголовком:

  • redelivered: true

что позволяет отличить первичную доставку от повторной.


Обработка ошибок и стратегия отклонения

Отклонение сообщения обычно связано с ошибками бизнес-логики или инфраструктуры.

Типовые сценарии:

Ошибка валидации данных

Сообщение содержит некорректную структуру или значения.

Реакция:

  • nack() без повторной обработки;
  • либо отправка в DLQ через брокер.

Временная ошибка обработки

Например, недоступность внешнего API.

Реакция:

  • nack() с ожиданием повторной доставки;
  • либо задержка обработки через очередь retry.

Неизвестная ошибка выполнения

Нестабильное состояние приложения или исключение.

Реакция:

  • nack() с последующим анализом логов;
  • ограничение количества повторов через брокер.

Транзакционное подтверждение и отклонение

В STOMP поддерживаются транзакции, позволяющие группировать операции подтверждения.

const tx = client.begin();

message.ack({ transaction: tx.id });

tx.commit();

При отклонении:

const tx = client.begin();

message.nack({ transaction: tx.id });

tx.rollback();

Особенности транзакционного режима:

  • подтверждения фиксируются только после commit;
  • rollback отменяет ack/nack операции;
  • используется для атомарной обработки пачек сообщений.

Заголовки, влияющие на отклонение

При работе с STOMP.js важны метаданные сообщения:

  • message-id — идентификатор сообщения;
  • subscription — идентификатор подписки;
  • ack — режим подтверждения;
  • redelivered — признак повторной доставки.

Эти поля используются брокером для отслеживания состояния доставки и принятия решения о повторной отправке.


Поведение STOMP.js при nack

В STOMP.js объект сообщения предоставляет интерфейс:

message.nack({
  headers: {
    // дополнительные параметры, если поддерживаются брокером
  }
});

Возможные последствия:

  • сообщение возвращается в очередь;
  • увеличивается счетчик повторных доставок;
  • инициируется DLQ-процесс;
  • соединение остаётся активным без ошибок на клиенте.

Важно, что STOMP.js не реализует логику повторов самостоятельно — он только отправляет протокольную команду брокеру.


Очереди повторных попыток и dead-letter механизмы

При многократных отклонениях сообщение может попадать в специальные очереди.

Retry queue

Используется для:

  • отложенных повторных обработок;
  • ограниченного числа попыток доставки;
  • экспоненциальной задержки.

Dead-letter queue (DLQ)

Финальная точка маршрута сообщений:

  • сообщение признано необрабатываемым;
  • сохраняется для анализа;
  • может быть обработано вручную или отдельным сервисом.

Параллельная обработка и риск неправильного отклонения

При использовании CLIENT_INDIVIDUAL или CLIENT_ACK в многопоточной обработке возникают типовые проблемы:

  • подтверждение уже обработанного сообщения;
  • повторный nack одного и того же сообщения;
  • гонки между обработчиками;
  • потеря состояния при асинхронной обработке.

Корректная модель требует:

  • строгой привязки ack/nack к жизненному циклу обработки;
  • исключения двойного подтверждения;
  • контроля конкурентного доступа к сообщению.

Типовые ошибки при использовании nack

Отсутствие ack/nack

Сообщение зависает в состоянии unacked и повторяется после реконнекта.

Неправильный выбор режима подписки

AUTO_ACK исключает возможность отклонения, несмотря на вызов nack().

Игнорирование redelivered

Отсутствие обработки повторных доставок приводит к бесконечным циклам обработки.

Перегрузка очереди повторов

Частое использование nack без анализа причины приводит к росту backlog.


Семантика отклонения в распределённых системах

Отклонение сообщений является частью модели at-least-once delivery:

  • сообщение может быть доставлено более одного раза;
  • nack увеличивает вероятность повторной доставки;
  • обработка должна быть идемпотентной;
  • брокер не гарантирует уникальность доставки при сбоях.

В STOMP.js эта модель проявляется особенно явно, так как клиентская библиотека не хранит состояние доставки, а только передаёт команды брокеру.