Обработчики ошибок

Надёжная обработка ошибок в STOMP.js строится на нескольких уровнях: транспортный (WebSocket), протокольный (STOMP frames), уровень клиента (логика обработки сообщений) и уровень брокера (ошибки очередей и подписок). Игнорирование любого из этих уровней приводит к “тихим” сбоям, разрывам соединения и потере сообщений без явных признаков причины.

Ошибки WebSocket-уровня

STOMP.js использует WebSocket как транспорт, поэтому любые проблемы соединения сначала проявляются именно здесь.

Основные точки обработки:

  • потеря соединения
  • ошибки handshake
  • сетевые сбои
  • закрытие соединения сервером

В STOMP.js за это отвечают события клиента:

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

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',

  onWebSocketError: (event) => {
    console.error('WebSocket ошибка:', event);
  },

  onWebSocketClose: (event) => {
    console.warn('WebSocket соединение закрыто:', event.code, event.reason);
  }
});

Особенности WebSocket ошибок

WebSocket-ошибки не содержат бизнес-логики STOMP. Они не дают информации о подписках или очередях. Это исключительно транспортный уровень.

Закрытие соединения может происходить:

  • по таймауту сервера
  • из-за сетевого разрыва
  • при рестарте брокера
  • при ошибке авторизации на handshake этапе

Для восстановления соединения используется:

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',
  reconnectDelay: 5000
});

reconnectDelay активирует автоматический реконнект после разрыва.


Ошибки STOMP-протокола (ERROR frame)

STOMP-протокол предусматривает специальный frame ERROR, который отправляется брокером при логических ошибках: неправильные подписки, ошибки авторизации, отказ в доступе к destination.

В STOMP.js это обрабатывается через:

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',

  onStompError: (frame) => {
    console.error('STOMP ошибка');
    console.error('Заголовки:', frame.headers);
    console.error('Сообщение:', frame.body);
  }
});

Структура ERROR frame

Обычно содержит:

  • message — краткое описание
  • content-type
  • тело с деталями ошибки

Пример:

message: "Unauthorized"
body: "User not allowed to access /queue/orders"

Причины появления STOMP ERROR

  • отсутствие прав на destination
  • неверный login/pass
  • отсутствие очереди на брокере
  • нарушение политики broker (RabbitMQ, ActiveMQ)
  • неверный формат subscribe/destination

Ошибки подключения (connection lifecycle)

Подключение STOMP проходит несколько стадий: создание WebSocket, handshake, CONNECT frame, подтверждение CONNECTED.

Ошибки могут возникать на любом этапе.

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',

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

  onStompError: (frame) => {
    console.error('Ошибка STOMP:', frame.body);
  }
});

Частые проблемы подключения

  1. Сервер не принимает WebSocket
  2. Неверный endpoint
  3. TLS/SSL mismatch (ws vs wss)
  4. Ошибка авторизации CONNECT frame
  5. Прокси обрывает соединение

Ошибки подписок (subscribe lifecycle)

Подписка в STOMP.js не гарантирует успешную регистрацию на брокере. Ошибки могут проявляться как:

  • отсутствие сообщений
  • ERROR frame от брокера
  • закрытие соединения
  • silent failure (редко, но встречается в брокерах без строгой проверки)
const subscription = client.subscribe('/queue/orders', (message) => {
  console.log('Получено:', message.body);
});

Обработка ошибок внутри подписки

STOMP не предоставляет callback “onSubscribeError” напрямую. Поэтому обработка строится косвенно:

  • анализ ERROR frame
  • контроль receipt
  • контроль таймаутов

Receipt и контроль подтверждений

STOMP поддерживает механизм receipt, который позволяет отслеживать выполнение команд (SEND, SUBSCRIBE, UNSUBSCRIBE).

client.subscribe('/queue/orders', handler, {
  receipt: 'sub-001'
});

Обработка подтверждения:

client.onRece ipt = (frame) => {
  console.log('Receipt получен:', frame.headers['receipt-id']);
};

Если receipt не приходит, это индикатор:

  • потери соединения
  • сбоя брокера
  • отказа в обработке команды

Ошибки отправки сообщений (SEND frame)

Отправка сообщений может завершаться ошибкой не сразу, а на стороне брокера.

client.publish({
  destination: '/queue/orders',
  body: JSON.stringify({ id: 1 }),
  headers: {
    persistent: 'true'
  }
});

Ошибки проявляются через:

  • STOMP ERROR frame
  • отсутствие доставки
  • отклонение брокером

Причины отклонения SEND

  • отсутствие очереди
  • превышение лимита payload
  • нарушение ACL
  • неправильный content-type

Обработка ошибок в сообщениях

Даже при успешной доставке сообщения ошибка может возникнуть на уровне обработки payload.

client.subscribe('/queue/orders', (message) => {
  try {
    const data = JSON.parse(message.body);
    processOrder(data);
  } catch (e) {
    console.error('Ошибка обработки сообщения:', e);
  }
});

Типичные проблемы

  • некорректный JSON
  • неожиданный формат данных
  • частично повреждённые сообщения
  • несоответствие схемы

Heartbeat и скрытые разрывы соединения

Heartbeat используется для определения “живости” соединения.

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',
  heartbeatIncoming: 10000,
  heartbeatOutgoing: 10000
});

Если heartbeat не приходит:

  • соединение считается мёртвым
  • инициируется reconnect (если включён)
  • могут отсутствовать явные ошибки

Сценарии silent disconnect

  • NAT timeout
  • firewall idle kill
  • прокси разрывает long-lived connection
  • брокер не отправляет heart-beat frames

Обработка некорректных или неожиданных frames

STOMP.js позволяет отслеживать низкоуровневые проблемы через debug:

const client = new Client({
  debug: (str) => {
    console.log('DEBUG:', str);
  }
});

Это важно при:

  • анализе нестандартных брокеров
  • отладке RabbitMQ / ActiveMQ
  • выявлении нарушений протокола

Очередь необработанных ошибок (Unhandled scenarios)

Некоторые ошибки не попадают в стандартные callback-и:

  • неожиданный disconnect во время send
  • потеря подписки без уведомления
  • сбой обработки frame внутри клиента

Для контроля состояния используется комбинация:

  • onWebSocketClose
  • onStompError
  • heartbeat monitoring
  • application-level watchdog
let lastActivity = Date.now();

client.subscribe('/queue/orders', (msg) => {
  lastActivity = Date.now();
});

setInterval(() => {
  if (Date.now() - lastActivity > 30000) {
    console.warn('Нет активности от брокера');
  }
}, 5000);

Ошибки авторизации и сессии

Если брокер требует авторизацию, ошибки могут приходить на этапе CONNECT:

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',
  connectHeaders: {
    login: 'user',
    passcode: 'wrong-password'
  },

  onStompError: (frame) => {
    console.error('Auth ошибка:', frame.body);
  }
});

Типичные сценарии

  • истёкший токен
  • неправильные credentials
  • отсутствие роли доступа
  • invalid session cookie

Разделение критических и некритических ошибок

Ошибки в STOMP.js условно делятся на два класса:

Критические

  • разрыв WebSocket
  • ERROR frame с закрытием соединения
  • сбой handshake
  • потеря heartbeat

Некритические

  • ошибка обработки message body
  • логическая ошибка бизнес-слоя
  • отказ отдельной очереди без разрыва соединения

Стратегии устойчивой обработки ошибок

Используется комбинация механизмов:

  • автоматический reconnect
  • heartbeat monitoring
  • retry логика отправки сообщений
  • централизованная обработка onStompError
  • контроль активности подписок
const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',
  reconnectDelay: 5000,
  heartbeatIncoming: 10000,
  heartbeatOutgoing: 10000,

  onStompError: (frame) => {
    logError(frame.body);
  },

  onWebSocketClose: () => {
    notify('connection lost');
  }
});

При построении устойчивых систем STOMP.js обычно рассматривается не как транспорт “с гарантией доставки”, а как слой, требующий внешнего контроля состояния и повторных попыток на уровне приложения.