Класс Client

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

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

Экземпляр Client инкапсулирует всю сетевую логику и предоставляет высокоуровневый API для обмена сообщениями.


Создание экземпляра Client

Базовое создание клиента:

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

const client = new Client();

После создания объект ещё не инициирует подключение. Соединение начинается только после вызова метода activate().


Конструктор Client

Класс поддерживает передачу конфигурации напрямую в конструктор:

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

Такой подход позволяет сразу определить параметры подключения и поведение клиента.


Свойство brokerURL

Свойство brokerURL задаёт адрес WebSocket-соединения.

Пример:

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

Поддерживаются:

  • ws://
  • wss://

Использование wss:// обязательно в production-среде при HTTPS.

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

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

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

Вместо brokerURL можно использовать фабрику WebSocket.

Это особенно полезно:

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

Пример:

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

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

SockJS обеспечивает fallback-транспорт при отсутствии полноценного WebSocket.

Пример:

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

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

В этом режиме brokerURL не используется.


Метод activate

Метод activate() инициирует подключение:

client.activate();

После вызова:

  1. создаётся WebSocket;
  2. выполняется STOMP CONNECT;
  3. запускаются heartbeat;
  4. активируются механизмы reconnect.

Состояния клиента

Клиент имеет внутреннее состояние жизненного цикла.

Основные этапы:

INACTIVE
ACTIVE
DEACTIVATING

Проверка активности:

console.log(client.active);

Если значение true, клиент активен либо находится в процессе подключения.


Обработчик onConnect

onConnect вызывается после успешного STOMP CONNECT.

Пример:

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

Именно внутри onConnect обычно создаются подписки.


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

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

client.onConn ect = () => {

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

};

client.activate();

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


Обработчик onDisconnect

Срабатывает после DISCONNECT:

client.onDisconn ect = () => {
    console.log('Disconnected');
};

Этот обработчик не вызывается при физическом разрыве WebSocket без STOMP DISCONNECT.


Обработчик onStompError

Позволяет обработать ERROR frame от брокера.

Пример:

client.onStompEr ror = (frame) => {

    console.error('Broker error');
    console.error(frame.headers['message']);
    console.error(frame.body);

};

Наиболее частые причины:

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

Обработчик onWebSocketError

Отлавливает ошибки WebSocket:

client.onWebSocketEr ror = (event) => {
    console.error('WebSocket error', event);
};

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

  • недоступности сервера;
  • проблемах TLS;
  • сетевых сбоях;
  • ошибках прокси.

Обработчик onWebSocketClose

Вызывается при закрытии соединения:

client.onWebSocketCl ose = (event) => {
    console.log('Connection closed');
};

Через объект event можно получить:

event.code
event.reason

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

Параметр reconnectDelay включает reconnect.

Пример:

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

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


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

const client = new Client({
    reconnectDelay: 0
});

В этом случае клиент не будет восстанавливать соединение автоматически.


Heartbeat

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

Настройка:

const client = new Client({
    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000
});

heartbeatIncoming

Интервал ожидания heartbeat от сервера.

heartbeatOutgoing

Интервал отправки heartbeat серверу.


Работа heartbeat

Если heartbeat не приходит в течение заданного времени:

  1. соединение считается потерянным;
  2. WebSocket закрывается;
  3. запускается reconnect.

Это позволяет обнаруживать “зависшие” TCP-соединения.


Debug logging

Для диагностики используется свойство debug.

Пример:

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

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

  • CONNECT;
  • SUBSCRIBE;
  • MESSAGE;
  • RECEIPT;
  • reconnect;
  • heartbeat.

Отключение debug

client.debug = () => {};

В production debug обычно отключают.


Публикация сообщений

Метод publish отправляет сообщения брокеру.

Пример:

client.publish({
    destination: '/queue/test',
    body: 'Hello'
});

Заголовки publish

Можно передавать заголовки:

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

Binary payload

STOMP.js поддерживает бинарные данные.

Пример:

const bytes = new Uint8Array([1, 2, 3]);

client.publish({
    destination: '/queue/binary',
    binaryBody: bytes
});

Подписка через subscribe

Базовый пример:

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

Отмена подписки

subscription.unsubscribe();

После отмены брокер прекращает доставку сообщений.


Ack режимы

Поддерживаются режимы:

  • auto
  • client
  • client-individual

Пример:

client.subscribe(
    '/queue/tasks',
    (message) => {

        console.log(message.body);

        message.ack();

    },
    {
        ack: 'client'
    }
);

message.ack

Подтверждение доставки:

message.ack();

После ACK брокер удаляет сообщение из очереди.


message.nack

Отрицательное подтверждение:

message.nack();

В зависимости от брокера сообщение может:

  • вернуться в очередь;
  • попасть в dead-letter queue;
  • быть отброшено.

RECEIPT frames

STOMP поддерживает подтверждение операций.

Пример:

client.watchForReceipt('msg-1', () => {
    console.log('Message delivered');
});

client.publish({
    destination: '/queue/test',
    body: 'Hello',
    headers: {
        receipt: 'msg-1'
    }
});

deactivate

Корректное завершение соединения:

await client.deactivate();

Метод:

  1. отправляет DISCONNECT;
  2. закрывает heartbeat;
  3. завершает reconnect;
  4. закрывает WebSocket.

forceDisconnect

Принудительное закрытие:

client.forceDisconnect();

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

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

deactivate и forceDisconnect

Разница между методами:

Метод STOMP DISCONNECT Закрытие WebSocket Graceful shutdown
deactivate Да Да Да
forceDisconnect Нет Да Нет

connectHeaders

Позволяет передавать заголовки CONNECT frame.

Пример:

const client = new Client({
    brokerURL: 'ws://localhost:15674/ws',
    connectHeaders: {
        login: 'admin',
        passcode: 'admin'
    }
});

JWT авторизация

Частый сценарий:

const client = new Client({
    brokerURL: 'wss://example.com/ws',
    connectHeaders: {
        Authorization: 'Bearer token'
    }
});

beforeConnect

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

Пример:

client.beforeConnect = async () => {

    const token = await loadToken();

    client.connectHeaders = {
        Authorization: `Bearer ${token}`
    };

};

Особенно важно при refresh JWT.


reconnectDelay и нестабильные сети

При мобильных сетях reconnect часто комбинируют с heartbeat:

const client = new Client({
    reconnectDelay: 3000,
    heartbeatIncoming: 10000,
    heartbeatOutgoing: 10000
});

Это уменьшает количество ложных reconnect.


discardWebsocketOnCommFailure

Параметр принудительно уничтожает WebSocket при ошибках связи.

Пример:

const client = new Client({
    discardWebsocketOnCommFailure: true
});

Полезно при проблемах браузеров и proxy buffering.


splitLargeFrames

Некоторые брокеры плохо работают с крупными STOMP frame.

Параметр:

const client = new Client({
    splitLargeFrames: true
});

maxWebSocketChunkSize

Размер частей frame:

const client = new Client({
    splitLargeFrames: true,
    maxWebSocketChunkSize: 8 * 1024
});

logRawCommunication

Позволяет видеть сырые STOMP frame.

const client = new Client({
    logRawCommunication: true
});

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


appendMissingNULLonIncoming

Некоторые серверы нарушают STOMP framing.

Настройка:

const client = new Client({
    appendMissingNULLonIncoming: true
});

Используется как workaround для несовместимых брокеров.


Конфигурация production-клиента

Пример полноценной настройки:

const client = new Client({

    brokerURL: 'wss://example.com/ws',

    reconnectDelay: 5000,

    heartbeatIncoming: 10000,
    heartbeatOutgoing: 10000,

    connectHeaders: {
        Authorization: 'Bearer token'
    },

    debug: () => {}

});

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

После восстановления соединения подписки создаются заново.

Типичный подход:

client.onConn ect = () => {

    client.subscribe('/topic/chat', handler);

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

};

onConnect вызывается после каждого reconnect.


Ошибки повторных подписок

Нельзя хранить старые subscription-объекты после reconnect:

let subscription;

client.onConn ect = () => {
    subscription = client.subscribe('/topic/test', handler);
};

После reconnection предыдущая подписка уже невалидна.


Защита от дублирования подписок

Проблемный код:

client.onConn ect = () => {
    initSubscriptions();
};

Если initSubscriptions() не очищает старые обработчики, возможны:

  • утечки памяти;
  • дублирование сообщений;
  • рост количества callback.

deactivate во время reconnect

При вызове:

await client.deactivate();

останавливаются:

  • reconnect timer;
  • heartbeat;
  • WebSocket lifecycle.

Это важно при смене пользователя или logout.


Управление состоянием приложения

Часто используется отдельный слой:

class SocketService {

    constructor() {

        this.client = new Client({
            brokerURL: 'ws://localhost:8080/ws',
            reconnectDelay: 5000
        });

    }

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

    disconnect() {
        return this.client.deactivate();
    }

}

Такой подход:

  • упрощает поддержку;
  • централизует reconnect;
  • изолирует STOMP-логику;
  • снижает связанность компонентов.

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

В React/Vue/Angular экземпляр Client обычно:

  • создаётся один раз;
  • хранится в singleton-service;
  • используется всеми модулями приложения.

Создание множества клиентов одновременно приводит к:

  • избытку WebSocket;
  • нагрузке на брокер;
  • проблемам heartbeat;
  • конфликтам reconnect.

Поток выполнения activate

После вызова activate() происходит следующая последовательность:

WebSocket connect
↓
STOMP CONNECT
↓
CONNECTED frame
↓
onConnect
↓
SUBSCRIBE
↓
MESSAGE exchange

Поток выполнения deactivate

DISCONNECT
↓
Receipt ожидание
↓
Heartbeat stop
↓
WebSocket close
↓
INACTIVE

Типичные ошибки конфигурации

Подписка до activate

Ошибка:

client.subscribe('/topic/test', handler);
client.activate();

Подписка не будет зарегистрирована.


Повторный activate

Ошибка:

client.activate();
client.activate();

Может привести к гонкам reconnect и нескольким WebSocket.


Слишком маленький heartbeat

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

heartbeatIncoming: 1000

На нестабильных сетях это вызывает постоянные reconnect.


reconnectDelay без heartbeat

Если heartbeat отключён:

heartbeatIncoming: 0,
heartbeatOutgoing: 0

клиент может долго не замечать разрыв TCP-соединения.


Внутренняя архитектура Client

Класс Client управляет:

  • WebSocket transport;
  • сериализацией STOMP frame;
  • reconnect scheduler;
  • heartbeat scheduler;
  • subscription registry;
  • receipt tracking;
  • callback lifecycle.

Благодаря этому приложение работает с высокоуровневым API, не взаимодействуя напрямую с STOMP protocol frame.