Ленивая подписка в контексте STOMP.js представляет собой стратегию управления подписками на каналы сообщений, при которой подписка создаётся не в момент установления соединения, а только тогда, когда появляется фактическая потребность в получении данных. Такой подход переносит акцент с «предварительного подключения ко всему» на динамическое подключение к конкретным топикам по мере их использования.
В рамках STOMP-протокола каждая подписка является отдельной
операцией, привязанной к destination (например,
/topic/chat, /queue/notifications). Ленивая
модель предполагает, что эти операции инициируются по требованию, а не
заранее.
Основная проблема жадной модели подписок — избыточное потребление ресурсов. При подключении к большому количеству топиков одновременно возникают следующие эффекты:
Ленивая подписка решает эти проблемы за счёт сокращения числа активных подписок до минимально необходимого набора. Это особенно критично в SPA, где пользователь может никогда не открыть часть функциональных модулей, но соединение при этом уже установлено.
STOMP.js реализует подписки через метод клиента
subscribe, возвращающий объект подписки с методом
unsubscribe.
Типовой сценарий:
const subscription = client.subscribe('/topic/messages', (message) => {
const body = JSON.parse(message.body);
console.log(body);
});
Отписка:
subscription.unsubscribe();
Эта модель напрямую отражает команду STOMP SUBSCRIBE и
UNSUBSCRIBE, отправляемые брокеру.
Каждая подписка привязана к активному соединению. При разрыве соединения все подписки теряются и должны быть восстановлены при реконнекте, если это предусмотрено логикой приложения.
Ленивая подписка строится вокруг идеи отложенного вызова
subscribe до момента первого реального потребления
данных.
Базовый шаблон управления:
class LazyStompSubscription {
constructor(client, destination, handler) {
this.client = client;
this.destination = destination;
this.handler = handler;
this.subscription = null;
}
ensureSubscribed() {
if (!this.subscription) {
this.subscription = this.client.subscribe(
this.destination,
this.handler
);
}
}
unsubscribe() {
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = null;
}
}
}
Такой подход позволяет контролировать момент создания подписки и предотвращать преждевременное подключение к каналам.
Часто ленивая подписка связывается с событиями пользовательского интерфейса или жизненного цикла приложения:
const chatSubscription = new LazyStompSubscription(
client,
'/topic/chat',
(msg) => renderMessage(JSON.parse(msg.body))
);
// подписка активируется только при открытии чата
function onChatOpen() {
chatSubscription.ensureSubscribed();
}
В этом случае соединение с брокером может быть установлено заранее, но подписка создаётся только при входе в соответствующий раздел.
Ленивая подписка требует чёткого контроля жизненного цикла:
При использовании STOMP.js важно учитывать, что реконнект приводит к потере всех подписок, поэтому требуется механизм реинициализации:
function recreateSubscriptions() {
if (chatSubscription) {
chatSubscription.subscription = null;
}
}
В более сложных системах вводится registry подписок, который хранит метаданные и пересоздаёт активные подписки автоматически.
В SPA (React, Vue, Angular) ленивая подписка обычно привязывается к монтированию компонентов.
Пример логики:
useEffect(() => {
const sub = client.subscribe('/topic/notifications', handleMessage);
return () => sub.unsubscribe();
}, []);
Ленивая модель в этом случае проявляется не только как отложенное создание, но и как отсутствие глобальных подписок «на всякий случай».
Для масштабных приложений применяется реестр подписок:
class SubscriptionRegistry {
constructor(client) {
this.client = client;
this.map = new Map();
}
lazySubscribe(key, destination, handler) {
if (!this.map.has(key)) {
this.map.set(key, {
destination,
handler,
subscription: null
});
}
const entry = this.map.get(key);
if (!entry.subscription) {
entry.subscription = this.client.subscribe(
entry.destination,
entry.handler
);
}
return entry.subscription;
}
unsubscribe(key) {
const entry = this.map.get(key);
if (entry?.subscription) {
entry.subscription.unsubscribe();
entry.subscription = null;
}
}
}
Такой реестр позволяет централизованно управлять ленивыми подписками и избегать дублирования логики.
При ленивой подписке часто возникает состояние гонки между:
Типичная проблема — попытка подписаться до
client.connected.
Решение заключается в проверке состояния и очереди операций:
function safeSubscribe(client, destination, handler) {
if (!client.connected) {
client.onConn ect = () => {
client.subscribe(destination, handler);
};
return;
}
client.subscribe(destination, handler);
}
В более продвинутых реализациях используется очередь команд, которые выполняются после восстановления соединения.
Ленивая модель часто комбинируется с агрегацией:
/topic/chat.*)Это снижает число подписок даже при ленивом создании.
Дополнительный уровень оптимизации — временная деактивация подписок, а не их полное удаление:
if (inactive) {
subscription.unsubscribe();
cache.set(key, subscription);
}
Ленивая подписка особенно эффективна при наличии условий:
Пример с feature flag:
if (features.realtimeEnabled) {
chat.ensureSubscribed();
}
Поведение ленивых подписок напрямую влияет на нагрузку брокера STOMP:
Однако при слишком агрессивной ленивости возможно увеличение задержек первого сообщения из-за времени подписки, что требует балансировки между «горячими» и «холодными» каналами.
Часто используется кэш активных подписок для предотвращения повторного создания:
const active = new Set();
function lazySubscribe(client, topic, handler) {
if (active.has(topic)) return;
client.subscribe(topic, handler);
active.add(topic);
}
Такой подход предотвращает дублирование подписок при повторных входах в один и тот же раздел приложения.