Архитектура клиент-сервер

STOMP (Simple Text Oriented Messaging Protocol) выступает как прикладной протокол поверх WebSocket, обеспечивающий структурированное взаимодействие между клиентом и сервером через брокер сообщений. В контексте JavaScript-библиотеки STOMP.js клиентская сторона реализует отправку и получение сообщений, а серверная часть чаще всего представлена брокером (RabbitMQ, ActiveMQ, Apache Artemis или Spring WebSocket Message Broker).

Ключевая архитектурная особенность заключается в разделении системы на три слоя:

  • клиент STOMP (браузер или Node.js через STOMP.js)
  • транспортный канал (обычно WebSocket)
  • брокер сообщений (маршрутизация, очереди, топики)

WebSocket обеспечивает двунаправленный поток байтов, но не задаёт семантику сообщений. STOMP добавляет текстовые фреймы с командами, заголовками и телом, превращая поток в управляемый протокол взаимодействия.


Общая модель взаимодействия

STOMP реализует модель publish-subscribe и point-to-point через абстракции:

  • destination — логический адрес сообщения
  • topic — канал широковещательной рассылки
  • queue — очередь с доставкой одному потребителю

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

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

  1. Клиент устанавливает WebSocket соединение с сервером
  2. Поверх соединения выполняется STOMP CONNECT
  3. Сервер подтверждает установку сессии
  4. Клиент подписывается на destination
  5. Клиент отправляет сообщения через SEND
  6. Сервер маршрутизирует сообщения через брокер
  7. Подписчики получают MESSAGE

STOMP фреймы как основа протокола

STOMP использует текстовые фреймы следующей структуры:

COMMAND
header1:value1
header2:value2

body^@

Каждое сообщение состоит из:

  • команды (CONNECT, SEND, SUBSCRIBE и др.)
  • заголовков (метаданные маршрутизации и управления)
  • пустой строки-разделителя
  • тела сообщения
  • завершающего нулевого символа

Ключевым моментом является то, что STOMP не навязывает формат тела: это может быть JSON, XML или произвольная строка.


Роль STOMP.js в клиентской архитектуре

STOMP.js является реализацией клиента STOMP поверх WebSocket API браузера. Он не реализует транспорт и не является брокером. Его функции ограничены:

  • формирование STOMP-фреймов
  • управление подключением и переподключением
  • поддержка подписок
  • обработка входящих MESSAGE фреймов
  • heartbeat механизм

Таким образом, STOMP.js — это протокольный адаптер между приложением и транспортом.


Жизненный цикл соединения

1. Установка WebSocket

Сначала создаётся низкоуровневое соединение:

ws://server:port/ws

На этом этапе никакой логики сообщений ещё нет.


2. STOMP CONNECT

После установления WebSocket клиент отправляет фрейм:

CONNECT
accept-version:1.2
heart-beat:10000,10000

Сервер отвечает:

CONNECTED
version:1.2
heart-beat:10000,10000

На этом этапе создаётся STOMP-сессия, привязанная к WebSocket соединению.


3. Подписка на каналы

Клиент формирует подписку:

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

Каждая подписка имеет уникальный id, который используется для управления ACK и отмены подписки.


4. Передача сообщений

Отправка выполняется через SEND:

SEND
destination:/app/message

{"text":"hello"}

Важно, что destination интерпретируется серверной логикой. Часто используется разделение:

  • /app — входящие команды на сервер
  • /topic — broadcast от сервера
  • /queue — индивидуальные сообщения

5. Получение сообщений

Сервер отправляет клиенту:

MESSAGE
subscription:sub-0
message-id:001
destination:/topic/chat

{"text":"hello"}

STOMP.js преобразует это в callback подписки.


Маршрутизация сообщений на сервере

Серверная часть STOMP-архитектуры обычно включает брокер сообщений. Он выполняет:

  • маршрутизацию по destination
  • управление очередями
  • доставку сообщений подписчикам
  • обеспечение гарантии доставки (в зависимости от конфигурации)

Типы маршрутизации

Topic (pub/sub): Сообщение получают все подписчики канала.

Queue (point-to-point): Сообщение получает один из потребителей.


Heartbeat и контроль соединения

STOMP поддерживает механизм heartbeat для обнаружения разрывов соединения.

Формат:

heart-beat:client,server

Где:

  • client — интервал отправки ping от клиента
  • server — ожидаемый интервал от сервера

STOMP.js автоматически:

  • отправляет пустые кадры или ping
  • отслеживает задержки
  • инициирует переподключение при таймаутах

Внутренняя модель STOMP.js

STOMP.js можно рассматривать как конечный автомат состояний:

  • DISCONNECTED
  • CONNECTING
  • CONNECTED
  • RECONNECTING
  • DISCONNECTING

Каждое состояние определяет допустимые операции:

  • SEND возможен только при CONNECTED
  • SUBSCRIBE требует активной сессии
  • ACK зависит от настроек брокера

Обработка подписок

Каждая подписка в STOMP.js хранится как объект:

  • id подписки
  • destination
  • callback обработки сообщений
  • ACK режим (auto/client/client-individual)

ACK режим определяет, когда сообщение считается подтверждённым:

  • auto — автоматически после доставки
  • client — вручную через ACK
  • client-individual — подтверждение каждого сообщения отдельно

ACK и гарантии доставки

В архитектуре клиент-сервер STOMP ACK играет ключевую роль в надёжности доставки.

Сценарий:

  1. брокер отправляет MESSAGE
  2. клиент получает сообщение
  3. клиент вызывает ACK
  4. брокер удаляет сообщение из очереди

Если ACK не получен, сообщение может быть переотправлено.


Интеграция с серверными платформами

STOMP чаще всего используется в связке с:

Spring WebSocket Messaging

  • @MessageMapping маршрутизация
  • SimpMessagingTemplate для отправки
  • встроенный брокер или RabbitMQ

RabbitMQ STOMP plugin

  • поддержка STOMP поверх AMQP
  • прямое маппирование queue/topic

ActiveMQ

  • нативная поддержка STOMP
  • устойчивые очереди и транзакции

Логическая модель маршрутов

Маршруты STOMP можно представить как пространство имён:

/topic/public
/topic/user/{id}
/queue/tasks
/app/command

Каждый сегмент определяет поведение:

  • /topic — broadcast
  • /queue — очередь
  • /app — серверная обработка
  • /user — персонализированная маршрутизация

Конкурентность и многоподписочность

Один клиент STOMP.js может:

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

Архитектурно это реализуется через мультиплексирование одного WebSocket соединения.


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

Полный цикл сообщения:

  1. UI событие в браузере
  2. STOMP.js формирует SEND frame
  3. WebSocket передаёт данные
  4. серверный адаптер STOMP принимает фрейм
  5. брокер маршрутизирует сообщение
  6. подписчики получают MESSAGE frame
  7. STOMP.js вызывает callback
  8. UI обновляется

Масштабирование архитектуры

STOMP не масштабирует транспорт, он масштабирует логику доставки через брокер.

Типовые стратегии:

  • горизонтальное масштабирование брокеров
  • кластеризация очередей
  • sticky sessions для WebSocket
  • shared message bus

Ограничения архитектуры

Несмотря на универсальность, архитектура STOMP имеет особенности:

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

Взаимодействие с WebSocket уровнем

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

  • транспорт
  • двунаправленность
  • низкую задержку

STOMP добавляет:

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

Таким образом, STOMP можно рассматривать как прикладной слой над WebSocket, формирующий полноценную message-driven архитектуру.