Методы подключения

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

Основой работы библиотеки является протокол STOMP поверх WebSocket. Клиент устанавливает WebSocket-соединение, после чего начинает обмен STOMP-фреймами:

  • CONNECT
  • CONNECTED
  • SEND
  • SUBSCRIBE
  • MESSAGE
  • ACK
  • DISCONNECT

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


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

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

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

const client = new Client();

После создания экземпляра задаются параметры подключения:

client.brokerURL = 'ws://localhost:15674/ws';

Или:

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

Подключение через brokerURL

Свойство brokerURL является наиболее простым методом подключения.

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

client.activate();

Внутри библиотеки автоматически создаётся объект WebSocket.

Преимущества метода

  • минимальное количество кода;
  • автоматическое создание WebSocket;
  • удобная настройка reconnect;
  • хорошая совместимость с браузерами.

Недостатки

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

Использование ws:// и wss://

Подключение может выполняться через:

  • ws:// — обычный WebSocket;
  • wss:// — защищённый WebSocket через TLS.

Пример защищённого подключения:

const client = new Client({
    brokerURL: 'wss://broker.example.com/ws'
});

Когда использовать wss://

Защищённый транспорт обязателен:

  • в production-среде;
  • при использовании HTTPS;
  • при работе с авторизацией;
  • при передаче персональных данных;
  • при подключении через интернет.

Современные браузеры блокируют ws://, если страница открыта через HTTPS.


Активация подключения

После конфигурирования клиента вызывается метод:

client.activate();

Этот метод:

  1. создаёт WebSocket;
  2. открывает соединение;
  3. отправляет STOMP-фрейм CONNECT;
  4. ожидает ответ CONNECTED.

До вызова activate() соединение не устанавливается.


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

Для отслеживания успешного подключения используется onConnect.

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

    onConnect: () => {
        console.log('Connected');
    }
});

После подключения обычно:

  • создаются подписки;
  • отправляются стартовые сообщения;
  • инициализируется состояние приложения.

Передача заголовков CONNECT

STOMP позволяет передавать дополнительные заголовки.

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

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

Эти данные помещаются во фрейм:

CONNECT
login:admin
passcode:admin

Авторизация через токены

Во многих современных системах используются JWT-токены.

const token = localStorage.getItem('token');

const client = new Client({
    brokerURL: 'wss://api.example.com/ws',

    connectHeaders: {
        Authorization: `Bearer ${token}`
    }
});

Некоторые брокеры анализируют заголовки STOMP, а некоторые — HTTP-заголовки WebSocket handshake.


Подключение через webSocketFactory

Если требуется полный контроль над WebSocket, используется webSocketFactory.

const client = new Client({
    webSocketFactory: () => {
        return new WebSocket('ws://localhost:8080/ws');
    }
});

Этот механизм особенно важен:

  • в Node.js;
  • при использовании SockJS;
  • при кастомной авторизации;
  • при проксировании;
  • при работе через нестандартный транспорт.

Разница между brokerURL и webSocketFactory

brokerURL

new Client({
    brokerURL: 'ws://localhost/ws'
});

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

  • проще;
  • меньше кода;
  • WebSocket создаётся автоматически.

webSocketFactory

new Client({
    webSocketFactory: () => new WebSocket(url)
});

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

  • максимальная гибкость;
  • контроль над транспортом;
  • поддержка нестандартных решений.

Использование SockJS

Некоторые серверы работают через SockJS.

Для этого используется библиотека SockJS.

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

const client = new Client({
    webSocketFactory: () => {
        return new SockJS('http://localhost:8080/stomp');
    }
});

SockJS предоставляет fallback-механизмы:

  • xhr-streaming;
  • long polling;
  • iframe transports.

Когда нужен SockJS

SockJS полезен:

  • при поддержке старых браузеров;
  • при ограничениях корпоративных сетей;
  • при нестабильной поддержке WebSocket;
  • при использовании старых backend-решений.

В современных системах предпочтительнее чистый WebSocket.


Подключение в Node.js

В Node.js отсутствует встроенный WebSocket API браузера.

Используется пакет ws.

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

const client = new Client({
    webSocketFactory: () => {
        return new WebSocket('ws://localhost:15674/ws');
    }
});

Настройка reconnectDelay

STOMP.js поддерживает автоматическое переподключение.

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

Если соединение разрывается, библиотека повторит подключение через 5 секунд.


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

После потери соединения библиотека:

  1. закрывает старый сокет;
  2. очищает внутреннее состояние;
  3. ожидает reconnectDelay;
  4. создаёт новый WebSocket;
  5. выполняет повторный CONNECT.

Полное отключение reconnect

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

В этом случае переподключение отключается полностью.


Экспоненциальный backoff

В production-системах фиксированный reconnect может создавать перегрузку брокера.

Часто реализуется экспоненциальная задержка:

let reconnectTime = 1000;

const client = new Client({
    webSocketFactory: () => {
        return new WebSocket('ws://localhost:15674/ws');
    },

    reconnectDelay: reconnectTime,

    onWebSocketClose: () => {
        reconnectTime = Math.min(reconnectTime * 2, 30000);
    },

    onConnect: () => {
        reconnectTime = 1000;
    }
});

Heartbeat-механизм

STOMP поддерживает heartbeat-пакеты.

Настройка:

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

    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000
});

Принцип работы heartbeat

Клиент и сервер периодически отправляют служебные символы.

Если heartbeat перестаёт приходить:

  • соединение считается потерянным;
  • запускается reconnect;
  • старый сокет закрывается.

Входящий и исходящий heartbeat

heartbeatOutgoing

Частота отправки heartbeat клиентом.

heartbeatOutgoing: 4000

heartbeatIncoming

Ожидаемая частота heartbeat от сервера.

heartbeatIncoming: 4000

Отключение heartbeat

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

    heartbeatIncoming: 0,
    heartbeatOutgoing: 0
});

Обычно heartbeat отключают:

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

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

Для диагностики используются обработчики:

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

    onStompError: (frame) => {
        console.error(frame.headers['message']);
    },

    onWebSocketError: (event) => {
        console.error(event);
    }
});

Разница между onStompError и onWebSocketError

onWebSocketError

Срабатывает при транспортных ошибках:

  • разрыв TCP;
  • DNS ошибки;
  • TLS ошибки;
  • проблемы сети.

onStompError

Срабатывает при ошибках протокола STOMP:

  • неверная авторизация;
  • отказ подписки;
  • ошибки broker-side логики.

Диагностика соединения через debug

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

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

    debug: (message) => {
        console.log(message);
    }
});

STOMP.js начинает выводить:

  • CONNECT;
  • CONNECTED;
  • SEND;
  • MESSAGE;
  • heartbeat;
  • reconnect;
  • disconnect.

Полный пример подключения

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

const client = new Client({
    brokerURL: 'wss://broker.example.com/ws',

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

    reconnectDelay: 5000,

    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000,

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

    onConnect: () => {
        console.log('Connected');

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

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

    onStompError: (frame) => {
        console.error(frame.headers['message']);
    },

    onWebSocketError: (event) => {
        console.error(event);
    }
});

client.activate();

Деактивация клиента

Для корректного закрытия соединения используется:

client.deactivate();

Метод:

  • отправляет DISCONNECT;
  • завершает heartbeat;
  • закрывает WebSocket;
  • останавливает reconnect.

Асинхронная деактивация

Метод возвращает Promise.

await client.deactivate();

Это особенно важно:

  • перед уничтожением приложения;
  • при logout;
  • при смене пользователя;
  • при hot reload.

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

Свойство:

client.connected

Пример:

if (client.connected) {
    console.log('Connected');
}

Состояния WebSocket

Иногда требуется анализировать внутреннее состояние сокета.

client.webSocket.readyState

Возможные значения:

Состояние Значение
CONNECTING 0
OPEN 1
CLOSING 2
CLOSED 3

Подключение через прокси

В корпоративных сетях WebSocket может проходить через reverse proxy:

  • Nginx;
  • HAProxy;
  • Traefik;
  • Apache.

Критически важны:

  • поддержка Upgrade;
  • сохранение keep-alive;
  • корректный timeout;
  • поддержка WebSocket tunnel.

Пример Nginx-конфигурации

location /ws {
    proxy_pass http://localhost:15674;

    proxy_http_version 1.1;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";

    proxy_read_timeout 86400;
}

Особенности подключения к RabbitMQ

Для RabbitMQ обычно используется Web STOMP plugin.

Типичный URL:

ws://localhost:15674/ws

Необходимо включить:

rabbitmq-plugins enable rabbitmq_web_stomp

Особенности подключения к ActiveMQ

ActiveMQ поддерживает STOMP через отдельный transport connector.

Пример конфигурации:

<transportConnector 
    name="stomp"
    uri="stomp://0.0.0.0:61613"/>

WebSocket transport:

<transportConnector 
    name="websocket"
    uri="ws://0.0.0.0:61614"/>

Lazy connection

Иногда подключение откладывается до первого действия пользователя.

let initialized = false;

function connect() {
    if (!initialized) {
        client.activate();
        initialized = true;
    }
}

Подход снижает:

  • нагрузку;
  • число idle connections;
  • расход памяти брокера.

Singleton-подключение

В SPA-приложениях обычно используется единый экземпляр клиента.

class StompService {
    constructor() {
        this.client = new Client({
            brokerURL: 'ws://localhost:15674/ws'
        });
    }

    connect() {
        this.client.activate();
    }
}

export default new StompService();

Такой подход предотвращает:

  • дублирование соединений;
  • утечки сокетов;
  • множественные heartbeat;
  • лишние подписки.

Повторная активация после deactivate

После deactivate() допускается повторный запуск:

await client.deactivate();

client.activate();

STOMP.js создаст новый WebSocket и начнёт новый цикл подключения.


Типичные ошибки подключения

Неверный URL

brokerURL: 'http://localhost/ws'

Ошибка:

SyntaxError: Failed to construct 'WebSocket'

Должно использоваться:

ws://

или:

wss://

Mixed Content

Ошибка:

Mixed Content: blocked

Причина:

  • сайт открыт через HTTPS;
  • используется ws://.

Решение:

wss://

Брокер не поддерживает WebSocket

Ошибка:

WebSocket connection failed

Причины:

  • отключён WebSocket plugin;
  • неправильный порт;
  • неверный endpoint.

Отсутствие reconnect

После сетевого сбоя клиент остаётся отключённым.

Причина:

reconnectDelay: 0

Бесконечные reconnect-попытки

Причины:

  • неверный пароль;
  • неверный endpoint;
  • firewall;
  • брокер недоступен.

Рекомендуемая production-конфигурация

const client = new Client({
    brokerURL: 'wss://broker.example.com/ws',

    reconnectDelay: 5000,

    heartbeatIncoming: 10000,
    heartbeatOutgoing: 10000,

    connectHeaders: {
        Authorization: `Bearer ${token}`
    },

    debug: () => {}
});

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

  • защищённое соединение;
  • heartbeat;
  • reconnect;
  • отключённый debug в production;
  • token-based авторизация.