Особенности конфигурации под разные брокеры

STOMP.js работает поверх WebSocket или SockJS и реализует текстовый протокол STOMP (Simple/Streaming Text Oriented Messaging Protocol). Несмотря на единый API клиента, поведение системы сильно зависит от брокера сообщений, поскольку каждый сервер реализует STOMP-слой с собственными расширениями, ограничениями и соглашениями.

Ключевая особенность конфигурации заключается в том, что клиент STOMP.js не является универсальным «plug-and-play» решением. Он требует точной настройки:

  • URL подключения (WebSocket endpoint)
  • заголовков CONNECT frame
  • политик подписки (destination naming)
  • подтверждений сообщений (ack mode)
  • heartbeat и таймингов
  • особенностей виртуальных хостов и очередей

Разные брокеры по-разному трактуют STOMP-стандарт, поэтому одинаковый клиентский код может вести себя иначе.


Базовая структура конфигурации STOMP.js

Типовая конфигурация клиента выглядит следующим образом:

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

const client = new Client({
  brokerURL: 'ws://localhost:15674/ws',
  connectHeaders: {
    login: 'user',
    passcode: 'password',
  },
  debug: (str) => console.log(str),
  reconnectDelay: 5000,
  heartbeatIncoming: 10000,
  heartbeatOutgoing: 10000,
});

Однако значения этих параметров меняются в зависимости от брокера.


ActiveMQ Classic и Artemis: различия в STOMP-конфигурации

URL подключения

ActiveMQ и Artemis часто используют разные WebSocket-коннекторы:

  • ActiveMQ Classic:

    • ws://host:61614/stomp
    • иногда через Jetty WebSocket
  • Artemis:

    • ws://host:61614/ws
    • либо ws://host:8161/activemq-websocket

Ключевая особенность — наличие или отсутствие отдельного WebSocket transport.


Заголовки подключения

ActiveMQ часто требует минимальный набор:

connectHeaders: {
  login: 'admin',
  passcode: 'admin'
}

Artemis более строг в случае включенной security-domain и может требовать дополнительные headers:

connectHeaders: {
  login: 'user',
  passcode: 'pass',
  host: 'vhost1'
}

Поле host в STOMP frame становится критичным при использовании виртуальных хостов.


Префиксы очередей и топиков

ActiveMQ традиционно использует:

  • /queue/NAME
  • /topic/NAME

Artemis допускает более гибкую настройку:

  • /queue/
  • /topic/
  • /address/ (в зависимости от конфигурации broker address model)

Это влияет на формирование destination в подписках:

client.subscribe('/queue/orders', callback);

или

client.subscribe('/topic/events', callback);

Heartbeat и тайминги

ActiveMQ часто плохо работает с агрессивными heartbeat значениями. Рекомендуемая настройка:

heartbeatIncoming: 20000,
heartbeatOutgoing: 20000

Artemis, наоборот, корректно обрабатывает короткие интервалы:

heartbeatIncoming: 5000,
heartbeatOutgoing: 5000

RabbitMQ STOMP plugin: особенности конфигурации

RabbitMQ реализует STOMP через отдельный plugin, что существенно влияет на поведение клиента.


URL подключения

Типичный endpoint:

  • ws://localhost:15674/ws

Важно, что порт 15674 относится именно к STOMP-over-WebSocket плагину, а не к AMQP.


Virtual Host (vhost)

Ключевая особенность RabbitMQ — обязательная работа с vhost:

connectHeaders: {
  login: 'user',
  passcode: 'pass',
  host: '/'
}

Если vhost не указан, соединение может быть установлено, но подписки не будут работать.


Формат destination

RabbitMQ не использует строгие /queue и /topic как обязательные префиксы. Часто используется:

  • /queue/name
  • /exchange/exchange-name/routing-key
  • /amq/queue/name

Пример подписки:

client.subscribe('/queue/task_queue', callback);

Или через exchange:

client.subscribe('/exchange/logs/info', callback);

ACK режимы

RabbitMQ требует более внимательного управления подтверждениями сообщений.

Доступны режимы:

  • auto
  • client
  • client-individual

Пример:

client.subscribe('/queue/tasks', (message) => {
  console.log(message.body);
  message.ack();
}, { ack: 'client' });

Ошибка в ack-режиме может привести к бесконечной повторной доставке сообщений.


Heartbeat поведение

RabbitMQ STOMP plugin чувствителен к heartbeat и может разрывать соединение при несовпадении настроек между клиентом и сервером.

Рекомендуется синхронизация:

heartbeatOutgoing: 10000,
heartbeatIncoming: 10000

Apache Apollo (исторический брокер)

Apollo как STOMP-брокер имел достаточно «чистую» реализацию протокола, но с ограничениями на расширения.


Конфигурация подключения

brokerURL: 'ws://localhost:61623/stomp'

Apollo часто использовал нестандартные порты и требовал явного включения WebSocket acceptor.


Особенности маршрутизации

Apollo строго разделял:

  • /queue/
  • /topic/

и не поддерживал произвольные схемы destination без конфигурации broker.xml.


Ограничения

  • минимальная поддержка header extensions
  • ограниченная работа с durable subscriptions
  • строгая схема ack

HornetQ и переход к Artemis

HornetQ использовал STOMP как вторичный протокол и имел ряд ограничений, которые унаследовал Artemis в ранних версиях.

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

  • нестабильная WebSocket интеграция
  • чувствительность к heartbeat
  • необходимость строгого соответствия destination

Общие различия конфигурации STOMP.js между брокерами

1. Endpoint WebSocket

Главный источник проблем:

  • ActiveMQ: /stomp
  • Artemis: /ws
  • RabbitMQ: /ws
  • Apollo: /stomp

Ошибка в URL полностью блокирует соединение, даже при корректных credentials.


2. Аутентификация

Разные брокеры по-разному используют CONNECT headers:

Брокер login/passcode host (vhost) дополнительные поля
ActiveMQ да редко нет
Artemis да да иногда roles
RabbitMQ да обязательно нет

3. Формат сообщений и ack

STOMP.js предоставляет единый интерфейс, но:

  • RabbitMQ требует явного ack
  • ActiveMQ допускает auto-ack
  • Artemis поддерживает смешанные модели

4. Heartbeat несовместимость

Частая проблема:

  • клиент отправляет heartbeat
  • брокер не настроен на прием
  • соединение разрывается через 10–30 секунд

Решение всегда зависит от конкретного брокера, а не от STOMP.js.


5. Доставка сообщений и durability

Некоторые брокеры поддерживают durable subscriptions только при дополнительных header:

client.subscribe('/topic/news', callback, {
  id: 'sub-1',
  persistent: 'true'
});

Но такие расширения не являются частью стандарта STOMP и работают только на отдельных реализациях.


Практическая стратегия унификации конфигурации

При работе с несколькими брокерами одновременно используется слой адаптации конфигурации:

function createStompConfig(broker) {
  if (broker === 'rabbitmq') {
    return {
      brokerURL: 'ws://localhost:15674/ws',
      connectHeaders: {
        login: 'user',
        passcode: 'pass',
        host: '/'
      },
      heartbeatIncoming: 10000,
      heartbeatOutgoing: 10000
    };
  }

  if (broker === 'activemq') {
    return {
      brokerURL: 'ws://localhost:61614/stomp',
      connectHeaders: {
        login: 'admin',
        passcode: 'admin'
      },
      heartbeatIncoming: 20000,
      heartbeatOutgoing: 20000
    };
  }

  if (broker === 'artemis') {
    return {
      brokerURL: 'ws://localhost:61614/ws',
      connectHeaders: {
        login: 'admin',
        passcode: 'admin',
        host: 'vhost1'
      },
      heartbeatIncoming: 5000,
      heartbeatOutgoing: 5000
    };
  }
}

Конфигурационные ошибки, характерные для STOMP.js

Несовпадение destination

Одинаковое имя очереди может интерпретироваться по-разному:

  • /queue/test
  • test
  • /amq/queue/test

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

Особенно критично для RabbitMQ: соединение устанавливается, но подписки не работают.


Неправильный ack mode

Приводит к:

  • дублированию сообщений
  • потере сообщений
  • бесконечной повторной доставке

Несинхронизированный heartbeat

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

  • клиент отправляет heartbeat каждые 5 секунд
  • брокер ожидает 30 секунд
  • соединение закрывается

Итоговая инженерная модель конфигурации

STOMP.js в реальных системах рассматривается не как изолированный клиент, а как адаптер к конкретной реализации брокера. Конфигурация всегда включает три слоя:

  • транспортный слой (WebSocket endpoint)
  • протокольный слой (STOMP headers)
  • брокер-специфичный слой (vhost, destinations, ack, durability)

Именно различия в этих слоях формируют поведение системы при работе с ActiveMQ, Artemis, RabbitMQ и другими реализациями STOMP.