Что такое STOMP

STOMP — это текстовый протокол обмена сообщениями поверх TCP или WebSocket, предназначенный для взаимодействия клиентов с брокерами сообщений. Название расшифровывается как Simple Text Oriented Messaging Protocol.

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

  • RabbitMQ
  • Apache ActiveMQ
  • Apache Artemis
  • Apollo

Основная задача библиотеки — организация обмена сообщениями между клиентом и сервером в реальном времени.

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

  • чаты;
  • уведомления;
  • live-обновления;
  • биржевые данные;
  • игровые события;
  • синхронизация интерфейса;
  • совместная работа пользователей;
  • системы мониторинга.

Почему WebSocket недостаточно

WebSocket предоставляет постоянное двустороннее соединение между клиентом и сервером, однако сам по себе не определяет:

  • структуру сообщений;
  • маршрутизацию;
  • очереди;
  • подписки;
  • подтверждение доставки;
  • механизм брокера сообщений.

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

socket.send(JSON.stringify({
    type: "message",
    room: "general",
    payload: {
        text: "Hello"
    }
}));

STOMP решает эту проблему, вводя стандартизированный протокол поверх WebSocket.

Вместо произвольных JSON-структур используется единый формат кадров (frames), поддерживаемый брокерами сообщений.


Архитектура работы

Схема взаимодействия выглядит следующим образом:

Browser
   │
STOMP.js
   │
WebSocket
   │
Message Broker
   │
Queues / Topics

Клиент:

  1. Устанавливает WebSocket-соединение.
  2. Выполняет STOMP-подключение.
  3. Подписывается на каналы.
  4. Отправляет сообщения.
  5. Получает сообщения от брокера.

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

Текстовый протокол

Все команды передаются как текст.

Пример STOMP-кадра:

SEND
destination:/topic/chat

Hello world^@

^@ обозначает нулевой символ завершения кадра.


Человекочитаемость

STOMP легко анализировать вручную.

Например:

SUBSCRIBE
id:sub-1
destination:/topic/news

Протокол значительно проще бинарных решений.


Независимость от языка

STOMP поддерживается:

  • JavaScript;
  • Java;
  • Python;
  • Go;
  • PHP;
  • C#;
  • Ruby.

Это делает его удобным для микросервисной архитектуры.


Что такое брокер сообщений

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

Пример:

Client A → Broker → Client B

Клиенты не взаимодействуют напрямую.

Брокер отвечает за:

  • маршрутизацию;
  • очереди;
  • хранение;
  • подписки;
  • доставку;
  • подтверждение получения.

Очереди и топики

Queue

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

Producer → Queue → Consumer

Если подписчиков несколько — сообщение обычно получает только один.

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

  • задач;
  • фоновых операций;
  • worker-систем;
  • обработки заказов.

Topic

Topic предназначен для публикации сообщения всем подписчикам.

Publisher → Topic → All Subscribers

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

  • чатов;
  • уведомлений;
  • биржевых данных;
  • online-статусов.

Принцип Publish/Subscribe

STOMP активно использует модель Pub/Sub.

Компоненты:

Роль Назначение
Publisher Отправляет сообщения
Broker Распределяет сообщения
Subscriber Получает сообщения

Пример:

/topic/news

Подписчики получают все сообщения данного канала.


Установка STOMP.js

Современная версия библиотеки устанавливается через npm.

npm install @stomp/stompjs

Импорт:

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

Создание клиента

Базовая конфигурация:

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

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

brokerURL — адрес WebSocket-брокера.


Подключение к брокеру

Активация клиента:

client.activate();

После этого STOMP.js:

  1. Открывает WebSocket.
  2. Выполняет STOMP CONNECT.
  3. Ожидает CONNECTED.

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

client.onConn ect = () => {
    console.log('Connected');
};

Событие вызывается после успешного STOMP-подключения.


Отправка сообщений

Простейшая отправка:

client.publish({
    destination: '/topic/chat',
    body: 'Hello world'
});

Подписка на сообщения

client.subscribe('/topic/chat', (message) => {
    console.log(message.body);
});

message.body содержит тело сообщения.


Полный пример

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

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

client.onConn ect = () => {

    client.subscribe('/topic/chat', (message) => {
        console.log('Received:', message.body);
    });

    client.publish({
        destination: '/topic/chat',
        body: 'Hello from client'
    });
};

client.activate();

Что происходит внутри

После подключения STOMP.js отправляет:

CONNECT
accept-version:1.2
host:localhost

Брокер отвечает:

CONNECTED
version:1.2

После этого начинается обмен сообщениями.


STOMP Frames

Основная единица протокола — frame.

Структура:

COMMAND
header:value

Body^@

Компоненты:

Часть Назначение
COMMAND Тип операции
headers Метаданные
body Содержимое
null byte Завершение

Основные команды STOMP

CONNECT

Подключение клиента.

CONNECT
accept-version:1.2
host:localhost

CONNECTED

Подтверждение подключения.

CONNECTED
version:1.2

SEND

Отправка сообщения.

SEND
destination:/topic/chat

Hello

SUBSCRIBE

Подписка.

SUBSCRIBE
id:sub-0
destination:/topic/chat

MESSAGE

Сообщение от брокера.

MESSAGE
destination:/topic/chat

Hello client

DISCONNECT

Отключение.

DISCONNECT

WebSocket и STOMP

STOMP чаще всего работает поверх WebSocket.

STOMP.js автоматически использует:

WebSocket → STOMP → Broker

WebSocket обеспечивает транспорт.

STOMP обеспечивает:

  • команды;
  • подписки;
  • маршрутизацию;
  • стандартизацию.

SockJS и STOMP.js

Ранее STOMP.js часто использовался вместе с SockJS.

SockJS обеспечивал fallback-механизмы:

  • XHR-streaming;
  • long polling;
  • iframe transport.

Пример:

import SockJS from 'sockjs-client';
import { Client } from '@stomp/stompjs';

const client = new Client({
    webSocketFactory: () => new SockJS('/ws')
});

Сегодня в большинстве случаев достаточно обычного WebSocket.


Автоматическое переподключение

STOMP.js поддерживает reconnect.

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

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


Heartbeat

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

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

Подтверждение сообщений

STOMP поддерживает ACK/NACK.

Auto ACK

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

Подтверждение автоматически.


Client ACK

client.subscribe('/queue/tasks', callback, {
    ack: 'client'
});

Ручное подтверждение:

message.ack();

Форматы данных

Хотя STOMP текстовый, тело сообщения может содержать:

  • JSON;
  • XML;
  • HTML;
  • plain text;
  • base64.

JSON наиболее распространён.

client.publish({
    destination: '/topic/chat',
    body: JSON.stringify({
        user: 'Alex',
        text: 'Hello'
    })
});

Получение JSON

client.subscribe('/topic/chat', (message) => {

    const data = JSON.parse(message.body);

    console.log(data.user);
});

Headers

STOMP поддерживает заголовки.

client.publish({
    destination: '/topic/chat',
    headers: {
        priority: '9'
    },
    body: 'Important message'
});

Debug Mode

STOMP.js позволяет отслеживать весь протокол.

client.debug = (str) => {
    console.log(str);
};

Можно увидеть:

  • CONNECT;
  • SEND;
  • SUBSCRIBE;
  • heartbeat;
  • ошибки.

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

client.onStompEr ror = (frame) => {
    console.error(frame.headers['message']);
};

Отключение клиента

client.deactivate();

Библиотека:

  1. Отправляет DISCONNECT.
  2. Закрывает WebSocket.
  3. Очищает таймеры.

Пример чата

Отправка:

function sendMessage(text) {

    client.publish({
        destination: '/topic/chat',
        body: JSON.stringify({
            text
        })
    });
}

Получение:

client.subscribe('/topic/chat', (message) => {

    const data = JSON.parse(message.body);

    addMessageToUI(data.text);
});

Преимущества STOMP.js

Простота

API значительно проще низкоуровневого WebSocket.


Стандартизация

Протокол поддерживается множеством брокеров.


Работа через брокер

Не требуется самостоятельно реализовывать маршрутизацию сообщений.


Поддержка reconnect

Автоматическое восстановление соединения встроено в библиотеку.


Подписки

Pub/Sub реализован на уровне протокола.


Недостатки

Дополнительный уровень абстракции

STOMP добавляет overhead поверх WebSocket.


Текстовый формат

Бинарные протоколы иногда быстрее.


Зависимость от брокера

Требуется отдельный message broker.


Где используется STOMP.js

Онлайн-чаты

/topic/chat

Финансовые терминалы

Live-котировки и обновления.


Игры

Передача игровых событий.


Мониторинг

Системы логирования и метрик.


Совместное редактирование

Realtime-синхронизация документов.


Версии протокола

STOMP поддерживает версии:

  • 1.0
  • 1.1
  • 1.2

Современные брокеры обычно используют 1.2.


Отличие STOMP от MQTT

STOMP MQTT
Текстовый Бинарный
Простая структура Более компактный
WebSocket-friendly IoT-ориентирован
Прост в отладке Эффективнее по трафику

Отличие STOMP от AMQP

STOMP AMQP
Простой Сложный
Текстовый Бинарный
Лёгкий клиент Богатые возможности
Минимум логики Продвинутая маршрутизация

Внутренний жизненный цикл клиента

Последовательность работы:

activate()
    ↓
WebSocket open
    ↓
CONNECT
    ↓
CONNECTED
    ↓
SUBSCRIBE
    ↓
MESSAGE
    ↓
DISCONNECT

Когда STOMP.js подходит лучше всего

Библиотека особенно эффективна для:

  • realtime UI;
  • SPA-приложений;
  • dashboard-систем;
  • push-уведомлений;
  • live-обновлений;
  • микросервисной коммуникации;
  • брокеров сообщений;
  • event-driven архитектуры.

Когда STOMP.js использовать нежелательно

STOMP может быть не лучшим выбором при:

  • высокочастотных бинарных данных;
  • экстремально низких задержках;
  • IoT-устройствах с минимальным трафиком;
  • peer-to-peer соединениях без брокера;
  • потоковой передаче медиа.