Обработка успешного подключения

Соединение с брокером в STOMP.js завершается моментом получения серверного кадра CONNECTED, после которого клиент переходит в активное состояние и получает возможность отправлять сообщения, подписываться на очереди и участвовать в обмене данными. Обработка этого этапа является центральной точкой управления жизненным циклом клиента, так как именно здесь формируется контекст сессии и инициализируются прикладные подписки.

Успешное подключение фиксируется после выполнения STOMP handshake поверх транспортного уровня (обычно WebSocket или SockJS) и получения кадра протокола STOMP с командой CONNECTED. На этом этапе библиотека завершает внутреннюю инициализацию и вызывает callback, связанный с событием подключения.

В STOMP.js это реализуется через параметр onConnect (в новых версиях клиента) или через обработчик, передаваемый в метод activate().

Ключевая особенность заключается в том, что никакие операции протокола STOMP не должны выполняться до момента вызова этого обработчика. Любая попытка отправки сообщений или подписки до подключения приведёт к ошибкам или буферизации, зависящей от конфигурации клиента.

Структура обработчика успешного подключения

Функция обработки подключения получает два основных параметра:

  • frame — объект STOMP-фрейма CONNECTED
  • client-контекст уже активирован, но доступ к нему осуществляется через текущий экземпляр

Фрейм содержит метаданные соединения:

  • session — идентификатор сессии на стороне брокера
  • server — информация о сервере STOMP (если предоставляется)
  • heart-beat — параметры heartbeat, согласованные между клиентом и сервером
  • дополнительные заголовки, зависящие от брокера (RabbitMQ, ActiveMQ, Spring STOMP и т.д.)

Базовая модель подключения через activate()

Современный STOMP.js использует модель активации клиента:

import { Client } from '@stomp/stompjs';

const client = new Client({
  brokerURL: 'ws://localhost:8080/ws',
  reconnectDelay: 5000,
  heartbeatIncoming: 10000,
  heartbeatOutgoing: 10000,

  onConnect: (frame) => {
    console.log('Подключение установлено');
    console.log('Session ID:', frame.headers['session']);
  }
});

client.activate();

В момент вызова onConnect гарантируется, что транспорт уже установлен, STOMP handshake завершён и клиент находится в состоянии OPEN.

Использование session-id и заголовков CONNECTED

Одним из ключевых элементов успешного подключения является session-id. Он используется для:

  • трассировки соединений на сервере
  • отладки и мониторинга
  • связывания подписок с конкретным клиентом
  • реализации серверной маршрутизации сообщений

Доступ к заголовкам осуществляется через frame.headers:

onConnect: (frame) => {
  const sessionId = frame.headers['session'];
  const server = frame.headers['server'];

  console.log(sessionId);
  console.log(server);
}

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

Инициализация подписок после подключения

Наиболее типичный сценарий в обработчике успешного подключения — регистрация подписок. STOMP требует, чтобы подписки создавались только после установления соединения, так как каждая подписка привязывается к активной сессии.

onConnect: (frame) => {
  client.subscribe('/topic/messages', (message) => {
    console.log('Получено сообщение:', message.body);
  });
};

Подписка, созданная внутри onConnect, гарантированно привязана к активному session и будет корректно восстановлена при переподключении (если включена соответствующая логика клиента).

Роль heartbeat в успешном подключении

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

STOMP.js обрабатывает это автоматически, однако значение heartbeat можно получить косвенно через конфигурацию клиента или серверные заголовки.

heartbeatIncoming: 10000,
heartbeatOutgoing: 10000

Если сервер отклоняет предложенные значения, итоговая конфигурация согласуется в CONNECTED frame.

Состояния клиента после успешного подключения

После вызова onConnect клиент переходит в устойчивое состояние, в котором доступны операции:

  • publish() — отправка сообщений
  • subscribe() — подписка на очереди и топики
  • unsubscribe() — отмена подписок
  • управление транзакциями (если поддерживается брокером)

Внутренне клиент STOMP.js переходит из состояния CONNECTING в OPEN, что фиксируется в состоянии машины клиента.

Поведение при повторных подключениях

При включённом reconnectDelay успешное подключение может происходить многократно в течение жизненного цикла приложения. В таких случаях onConnect вызывается каждый раз заново, что требует идемпотентной инициализации подписок.

Типичная ошибка — дублирование подписок при каждом реконнекте. Это происходит, если не хранить ссылки на подписки:

let subscription;

onConnect: () => {
  if (!subscription) {
    subscription = client.subscribe('/topic/messages', handler);
  }
}

Более корректный подход — очистка старых подписок при переподключении или использование централизованного менеджера подписок.

Контекст ошибки и успешного соединения

Хотя обработка ошибок относится к другому этапу жизненного цикла, важно учитывать, что успешное подключение может сменяться разрывом без предварительного уведомления. Поэтому обработчик onConnect часто используется совместно с:

  • onStompError — ошибки протокола
  • onWebSocketClose — закрытие транспортного уровня
  • onWebSocketError — ошибки соединения WebSocket

Такой подход позволяет строить устойчивую модель реактивного подключения, где успешное соединение является не конечным состоянием, а началом активного цикла взаимодействия.

Использование готовности клиента как триггера бизнес-логики

После успешного подключения обычно выполняется инициализация прикладного слоя:

  • запрос начальных данных через publish/subscribe
  • регистрация пользователя в канале присутствия
  • отправка состояния клиента на сервер
  • восстановление ранее активных подписок

Пример:

onConnect: (frame) => {
  client.publish({
    destination: '/app/init',
    body: JSON.stringify({ type: 'INIT' })
  });

  client.subscribe('/topic/state', handlerState);
};

Такая схема делает CONNECTED точкой входа в всю событийную модель приложения.

Особенности поведения при нестабильной сети

При нестабильном соединении onConnect может вызываться многократно с короткими интервалами. В таких условиях критически важно:

  • избегать повторной регистрации одинаковых подписок
  • не дублировать отправку инициализационных сообщений
  • хранить состояние инициализации вне обработчика подключения

STOMP.js не гарантирует автоматическую дедупликацию логики приложения, поэтому контроль идемпотентности ложится на прикладной уровень.

Связь с транспортным уровнем

Хотя STOMP работает поверх WebSocket или SockJS, успешное подключение STOMP не означает стабильность транспортного уровня. Возможны ситуации, когда:

  • WebSocket открыт, но STOMP handshake не завершён
  • STOMP подключён, но heartbeat прерывается
  • соединение восстановлено, но с новой сессией

Поэтому обработчик успешного подключения следует рассматривать как точку синхронизации состояния, а не как гарантию долгосрочной стабильности канала.