В системах обмена сообщениями поверх STOMP.js устойчивость соединения определяется не только качеством транспортного уровня (WebSocket или SockJS), но и тем, как клиент и брокер обрабатывают разрывы, таймауты и частичную потерю состояния. Любой сбой в канале связи приводит к разрушению логического сеанса, включая подписки, непрочитанные сообщения и состояния подтверждений.
STOMP по своей природе не гарантирует автоматическое восстановление сессии. После разрыва соединения все подписки теряются, а брокер перестаёт учитывать клиент как активного потребителя. Поэтому восстановление после сбоев всегда реализуется на уровне клиентской логики.
Первый элемент восстановления — корректное определение факта сбоя. В STOMP.js это достигается через обработчики событий транспортного уровня и состояния клиента:
onConnect — успешное установление соединенияonDisconnect — штатное закрытиеonStompError — ошибка протокола STOMPonWebSocketClose — разрыв транспортного каналаonWebSocketError — ошибка сокетаКлючевое значение имеет именно событие закрытия WebSocket, так как оно фиксирует потерю канала независимо от состояния STOMP-сессии.
Типичная проблема заключается в том, что разрыв может быть «тихим» — без явной ошибки. В таких случаях используется heartbeat-механизм.
STOMP поддерживает heartbeat — периодическую отправку контрольных кадров между клиентом и брокером. В STOMP.js он задаётся при подключении:
heartbeatIncoming: 10000,
heartbeatOutgoing: 10000
Если в течение заданного интервала не приходит ожидаемый heartbeat, клиент интерпретирует это как разрыв соединения.
Особенности поведения:
Heartbeat не восстанавливает соединение, но служит триггером для запуска процедуры переподключения.
Переподключение не должно выполняться мгновенно и без ограничений. Типовая стратегия включает экспоненциальную задержку:
Такая модель предотвращает:
Пример логики:
let retryCount = 0;
function getDelay() {
return Math.min(30000, Math.pow(2, retryCount) * 1000);
}
При успешном подключении счётчик сбрасывается, что возвращает систему в нормальный режим работы.
Самая критичная часть восстановления — повторная регистрация подписок. После разрыва соединения брокер «забывает» все ранее созданные subscription.
Поэтому клиент обязан хранить локальный список активных подписок:
После переподключения выполняется реиграция:
subscriptions.forEach(sub => {
client.subscribe(sub.destination, sub.callback, { ack: sub.ackMode });
});
Особое внимание требуется к порядку подписки: некоторые брокеры начинают доставку сообщений сразу после ACK handshake, поэтому важно восстановить подписки до начала активной обработки бизнес-логики.
После сбоя возникает ключевая проблема: возможна потеря сообщений между моментом разрыва и повторного подключения.
STOMP сам по себе не обеспечивает exactly-once delivery. Поэтому используются дополнительные механизмы:
auto — сообщения считаются доставленными сразуclient — подтверждение вручную через
ack()client-individual — подтверждение каждого сообщения
отдельноДля устойчивых систем предпочтителен client-individual,
так как он минимизирует риск потери сообщений при сбое обработки.
client.ack(message);
client.nack(message);
Многие брокеры (RabbitMQ, ActiveMQ) поддерживают повторную доставку неподтверждённых сообщений после реконнекта. Это приводит к возможным дубликатам.
Следовательно, клиентская логика должна быть идемпотентной:
При разрыве соединения теряется не только подписка, но и контекст обработки:
Для восстановления используются следующие подходы:
const pending = new Map();
При получении сообщения оно добавляется в буфер до подтверждения. После ACK удаляется.
При реконнекте возможна повторная проверка состояния и очистка устаревших записей.
Если брокер поддерживает durable subscription, он сохраняет состояние подписки между сессиями. В этом случае клиент получает сообщения, накопленные за время отсутствия.
Однако STOMP.js не управляет этим поведением — оно задаётся на уровне брокера и заголовков подписки.
После реконнекта требуется повторная отправка CONNECT с теми же параметрами:
Если используется JWT или OAuth-токен, возможны дополнительные сценарии:
Ошибки аутентификации обрабатываются через onStompError,
после чего реконнект может быть заблокирован до обновления
credentials.
После успешного восстановления соединения возникает задача синхронизации:
Часто используется комбинация:
Типовой паттерн:
При массовых сбоях (например, падение брокера) клиенты могут одновременно начать реконнектиться, создавая нагрузку.
Для предотвращения используется jitter:
delay = baseDelay + Math.random() * 1000;
Также применяется глобальный backoff на уровне приложения, чтобы разные компоненты не инициировали реконнект синхронно.
Не все ошибки приводят к полному разрыву соединения. Возможны ситуации:
В таких случаях применяется принудительная ресинхронизация:
При длительных сбоях система должна различать:
Стратегия может включать:
Основная проблема после сбоя — расхождение состояния клиента и сервера. Даже при успешном переподключении возможны:
Решается через:
В STOMP.js восстановление соединения обычно реализуется вручную поверх клиента:
Это делает поведение гибким, но переносит всю ответственность за устойчивость на прикладной слой.
При мобильных и нестабильных сетях типичны сценарии:
В таких условиях важно:
Часто вводится state machine:
что позволяет исключить гонки состояний.
Поведение системы после сбоев формируется комбинацией нескольких уровней:
Только совместное использование этих механизмов обеспечивает восстановление без потери данных и неконтролируемых повторов сообщений.