Обратная совместимость в STOMP.js — способность библиотеки сохранять работоспособность старого клиентского кода после обновления версии. Для WebSocket-клиентов это особенно важно, поскольку STOMP.js часто используется в корпоративных системах, микросервисных платформах, финансовых приложениях, CRM, ERP и realtime-интерфейсах, где обновление фронтенда и брокера сообщений происходит независимо.
Нарушение обратной совместимости обычно приводит к следующим проблемам:
В экосистеме STOMP.js наиболее критичными считаются переходы:
stompjs на @stomp/stompjs;Исторически библиотека развивалась в нескольких направлениях:
| Поколение | Особенности |
|---|---|
stomp-websocket |
Старое API, callback-ориентированность |
stompjs |
Упрощённый клиент |
@stomp/stompjs |
Современный TypeScript-first клиент |
RxStomp |
Reactive-обёртка над STOMP.js |
Старые версии активно использовали:
Stomp.over(socket)
Современные версии делают акцент на классе Client:
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:15674/ws'
});
Главная проблема обратной совместимости заключается в том, что старая модель инициализации долгое время считалась стандартом де-факто.
Stomp.overРанее подключение создавалось так:
const socket = new WebSocket('ws://localhost:8080/ws');
const client = Stomp.over(socket);
client.connect({}, () => {
console.log('Connected');
});
Этот API присутствовал во множестве legacy-проектов.
Новый API:
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
onConnect: () => {
console.log('Connected');
}
});
client.activate();
Основные несовместимости:
| Старое API | Новое API |
|---|---|
connect() |
activate() |
disconnect() |
deactivate() |
reconnect_delay |
reconnectDelay |
| callback-стиль | объектная конфигурация |
debug = fn |
debug: fn |
STOMP.js сохраняет совместимость с несколькими версиями протокола.
| Версия | Особенности |
|---|---|
| STOMP 1.0 | нет heartbeat |
| STOMP 1.1 | heartbeat и ACK |
| STOMP 1.2 | улучшенная обработка заголовков |
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
stompVersions: new Versions(['1.0', '1.1', '1.2'])
});
Поддержка нескольких версий позволяет подключаться к старым брокерам без изменения серверной инфраструктуры.
Разные версии RabbitMQ STOMP plugin могут по-разному обрабатывать:
Некоторые legacy-конфигурации требуют отключения heartbeat:
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
heartbeatIncoming: 0,
heartbeatOutgoing: 0
});
Причина — старые версии плагина могли некорректно реагировать на heartbeat-кадры.
Старые брокеры иногда поддерживают только:
ack: 'client'
а не:
ack: 'client-individual'
Современный клиент должен учитывать подобные ограничения.
Старые версии ActiveMQ могут использовать особенности:
Некоторые старые ActiveMQ-конфигурации используют:
/topic/messages
другие:
/jms/topic/messages
Для совместимости приходится поддерживать оба формата.
Во многих старых проектах WebSocket отсутствовал или блокировался прокси-серверами. Использовался SockJS.
const socket = new SockJS('/ws');
const client = Stomp.over(socket);
const client = new Client({
webSocketFactory: () => new SockJS('/ws')
});
| Проблема | Причина |
|---|---|
| reconnect не работает | SockJS создаёт одноразовые соединения |
| heartbeat ломается | транспорт не поддерживает ping |
| нестабильные disconnect | transport fallback |
| polling transport | ограничения старых браузеров |
Ранние реализации STOMP.js поддерживали:
Современные версии библиотеки ориентированы на:
Для legacy-браузеров могут использоваться:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
Иногда необходим polyfill WebSocket:
global.WebSocket = require('ws');
Особенно это актуально для старых Node.js-приложений.
Старые версии STOMP.js изначально ориентировались на браузерную среду.
В Node.js приходилось вручную подключать WebSocket:
import WebSocket from 'ws';
const client = new Client({
webSocketFactory: () => new WebSocket('ws://localhost:8080/ws')
});
| Версия Node.js | Особенности |
|---|---|
| Node 10 | проблемы ES-модулей |
| Node 12 | ограниченная поддержка TS |
| Node 14+ | стабильная поддержка |
| Node 18+ | встроенный WebSocket |
Ранние версии использовали:
const Stomp = require('stompjs');
Современные версии:
import { Client } from '@stomp/stompjs';
Основные проблемы:
default export;let ClientLib;
try {
ClientLib = require('@stomp/stompjs');
} catch (e) {
ClientLib = window.Stomp;
}
Ранее использовалось свойство:
client.reconnect_delay = 5000;
В современных версиях:
const client = new Client({
reconnectDelay: 5000
});
Старые версии:
Новые версии:
Старые версии heavily relied on callbacks.
client.connect(
login,
passcode,
onConnect,
onError
);
const client = new Client({
connectHeaders: {
login,
passcode
},
onConnect,
onStompError
});
Основные сложности:
| Проблема | Причина |
|---|---|
потеря this |
изменение контекста |
| race condition | async activate |
| reconnect loop | другой lifecycle |
| duplicate subscription | повторная инициализация |
Старые проекты часто хранили подписки так:
const subscription = client.subscribe('/topic/test', callback);
Позже:
subscription.unsubscribe();
Современные версии сохранили этот API, что стало важным элементом обратной совместимости.
Ранние версии STOMP.js работали преимущественно со строками:
client.publish({
destination: '/topic/chat',
body: JSON.stringify(data)
});
Современные версии поддерживают:
Legacy-системы могут:
content-length;Heartbeat — один из наиболее проблемных элементов обратной совместимости.
client.heartbeat.outgoing = 20000;
client.heartbeat.incoming = 20000;
const client = new Client({
heartbeatIncoming: 20000,
heartbeatOutgoing: 20000
});
| Причина | Последствие |
|---|---|
| старый proxy | разрыв соединения |
| медленный broker | false disconnect |
| SockJS polling | heartbeat потеря |
| мобильные сети | jitter |
Часто создаётся промежуточный слой:
class LegacyStompAdapter {
constructor(url) {
this.client = new Client({
brokerURL: url
});
}
connect(headers, callback) {
this.client.onConn ect = callback;
this.client.activate();
}
disconnect(callback) {
this.client.deactivate().then(callback);
}
}
Хорошей практикой считается:
/api/v1/ws
/api/v2/ws
Это позволяет поддерживать старые frontend-клиенты параллельно с новыми.
Вместо проверки версии библиотеки используется:
if (client.activate) {
client.activate();
} else {
client.connect();
}
Подобный подход устойчивее к обновлениям.
Во многих проектах встречаются старые параметры:
client.debug = null;
client.ws = socket;
client.maxWebSocketChunkSize = 8 * 1024;
Современные версии могут:
Современные версии STOMP.js используют механизм устаревания API.
Пример:
console.warn(
'connect() is deprecated. Use activate().'
);
Такая политика позволяет постепенно мигрировать кодовые базы без мгновенного разрушения совместимости.
Старые клиенты:
body: JSON.stringify(data)
Новые системы могут использовать:
binaryBody: uint8Array
Если backend ожидает исключительно текстовые payload, бинарная передача вызывает несовместимость.
Некоторые старые версии позволяли переопределять внутренние методы:
client._transmit = function() {
// custom logic
};
Современные версии скрывают внутреннюю реализацию.
Это нарушает совместимость кастомных расширений.
Ранние проекты использовали JavaScript без типизации.
Современный STOMP.js содержит TypeScript-описания:
const client: Client = new Client();
| Проблема | Причина |
|---|---|
| implicit any | старый JS-код |
| incompatible callback | строгие типы |
| nullable socket | strict mode |
| missing headers type | интерфейсы |
В legacy-приложениях встречаются конфигурации:
window.STOMP_CONFIG = {
url: 'ws://localhost:8080/ws',
reconnect: true
};
Современные приложения чаще используют:
export default {
brokerURL: import.meta.env.VITE_WS_URL
};
Для совместимости нередко поддерживаются оба формата одновременно.
Наиболее безопасная стратегия включает:
import { Client } from '@stomp/stompjs';
export class CompatibleStompClient {
constructor(url) {
this.client = new Client({
brokerURL: url,
reconnectDelay: 5000
});
}
connect(headers, onConnect, onError) {
this.client.connectHeaders = headers;
this.client.onConn ect = onConnect;
this.client.onStompEr ror = onError;
this.client.activate();
}
disconnect(callback) {
this.client.deactivate()
.then(() => {
if (callback) {
callback();
}
});
}
subscribe(destination, callback) {
return this.client.subscribe(
destination,
callback
);
}
send(destination, headers, body) {
this.client.publish({
destination,
headers,
body
});
}
}
Такой слой позволяет запускать старый код практически без изменений, одновременно используя современную версию STOMP.js внутри системы.