Приложения, использующие WebSocket-соединения и брокеры сообщений, работают в условиях постоянного обмена событиями. При высокой нагрузке ошибки могут накапливаться незаметно: соединения начинают разрываться, сообщения теряются, задержки увеличиваются, а клиенты уходят в бесконечные переподключения. Без системы мониторинга подобные проблемы становятся заметны только после жалоб пользователей.
STOMP.js предоставляет механизмы, позволяющие отслеживать:
Мониторинг строится как на уровне клиента STOMP.js, так и на уровне брокера сообщений.
Одной из ключевых задач является контроль текущего состояния клиента.
В STOMP.js объект клиента содержит множество событий жизненного цикла:
import { Client } fr om '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
reconnectDelay: 5000
});
client.onConn ect = () => {
console.log('Подключение установлено');
};
client.onDisconn ect = () => {
console.log('Соединение закрыто');
};
client.onStompEr ror = (frame) => {
console.error('Ошибка STOMP', frame.headers['message']);
};
client.onWebSocketCl ose = () => {
console.log('WebSocket закрыт');
};
client.onWebSocketEr ror = (event) => {
console.error('Ошибка WebSocket', event);
};
client.activate();
Такой подход позволяет централизованно отслеживать состояние транспортного уровня и логики STOMP-протокола.
Во многих приложениях требуется единая модель состояния соединения.
Например:
const connectionState = {
connected: false,
reconnects: 0,
lastError: null
};
client.onConn ect = () => {
connectionState.connected = true;
};
client.onWebSocketCl ose = () => {
connectionState.connected = false;
};
client.onStompEr ror = (frame) => {
connectionState.lastError = frame.headers['message'];
};
Подобная структура может использоваться:
Heartbeat — механизм проверки активности соединения между клиентом и брокером.
Настройка heartbeat:
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
heartbeatIncoming: 10000,
heartbeatOutgoing: 10000
});
Здесь:
heartbeatIncoming — ожидание heartbeat от сервера;heartbeatOutgoing — отправка heartbeat серверу.Если heartbeat перестают приходить, соединение считается неактивным.
Иногда необходимо самостоятельно отслеживать время последней активности.
let lastHeartbeat = Date.now();
client.onHeartbeatRecei ved = () => {
lastHeartbeat = Date.now();
};
setInterval(() => {
const diff = Date.now() - lastHeartbeat;
if (diff > 30000) {
console.error('Heartbeat timeout');
}
}, 5000);
Такой механизм особенно полезен:
STOMP.js поддерживает встроенное логирование.
client.debug = (message) => {
console.log('[STOMP]', message);
};
В логах появляются:
Пример вывода:
[STOMP] Opening Web Socket...
[STOMP] Web Socket Opened...
[STOMP] >>> CONNECT
accept-version:1.2
heart-beat:10000,10000
В production-среде console.log недостаточен.
Логи обычно передаются:
Пример:
client.debug = (message) => {
sendToMonitoringSystem({
service: 'chat-service',
level: 'info',
message,
timestamp: Date.now()
});
};
Частые reconnect-события указывают на сетевые проблемы или перегрузку брокера.
let reconnectCounter = 0;
client.onWebSocketCl ose = () => {
reconnectCounter++;
console.log('Reconnects:', reconnectCounter);
};
Дополнительно можно собирать:
Иногда соединение устанавливается слишком долго.
const startedAt = Date.now();
client.onConn ect = () => {
const duration = Date.now() - startedAt;
console.log('Connection time:', duration);
};
Высокое время подключения может означать:
Одним из важнейших показателей является latency.
Отправитель добавляет timestamp:
client.publish({
destination: '/topic/metrics',
body: JSON.stringify({
timestamp: Date.now()
})
});
Получатель вычисляет задержку:
client.subscribe('/topic/metrics', (message) => {
const data = JSON.parse(message.body);
const latency = Date.now() - data.timestamp;
console.log('Latency:', latency);
});
На стороне брокера важно отслеживать:
Например, в RabbitMQ доступны:
При использовании ручного подтверждения сообщений необходимо отслеживать ACK/NACK.
client.subscribe('/queue/orders', (message) => {
try {
processOrder(message.body);
message.ack();
} catch (error) {
message.nack();
}
}, {
ack: 'client'
});
Можно вести статистику:
const metrics = {
ack: 0,
nack: 0
};
Ошибки бизнес-логики должны отделяться от сетевых ошибок.
client.subscribe('/queue/tasks', (message) => {
try {
handleTask(message.body);
} catch (error) {
taskMetrics.failed++;
logError(error);
}
});
В production обычно анализируются:
Некоторые подписки могут потреблять значительно больше ресурсов.
Пример измерения времени обработки:
client.subscribe('/topic/events', (message) => {
const started = performance.now();
handleEvent(message.body);
const ended = performance.now();
console.log('Processing:', ended - started);
});
Полезные показатели:
Слишком большие payload могут вызывать:
Мониторинг размера:
client.subscribe('/topic/data', (message) => {
const size = new Blob([message.body]).size;
console.log('Message size:', size);
});
При долгоживущих WebSocket-соединениях возможно накопление утечек памяти.
Проблемы возникают из-за:
Пример опасного кода:
const cache = [];
client.subscribe('/topic/logs', (message) => {
cache.push(message.body);
});
Без ограничения массив будет расти бесконечно.
Безопасный вариант:
const cache = [];
const LIM IT = 1000;
client.subscribe('/topic/logs', (message) => {
cache.push(message.body);
if (cache.length > LIMIT) {
cache.shift();
}
});
Избыточные подписки создают дополнительную нагрузку.
const subscriptions = new Map();
function registerSubscription(topic) {
const sub = client.subscribe(topic, handler);
subscriptions.set(topic, sub);
}
Отслеживаются:
Ошибка:
setInterval(() => {
client.subscribe('/topic/test', handler);
}, 1000);
Каждую секунду создаётся новая подписка.
Правильный вариант:
let subscription = null;
if (!subscription) {
subscription = client.subscribe('/topic/test', handler);
}
Популярный подход — экспорт метрик в Prometheus.
Пример клиентских метрик:
const metrics = {
reconnects: 0,
messagesReceived: 0,
messagesSent: 0,
errors: 0
};
Далее сервер агрегирует показатели:
stomp_messages_received_total 10234
stomp_reconnects_total 12
stomp_errors_total 3
Grafana позволяет визуализировать:
Типичные панели:
В браузере доступны инструменты разработчика.
Во вкладке Network → WS можно анализировать:
Это позволяет быстро выявлять:
Клиентские события можно отправлять в систему аналитики.
function reportMetric(name, value) {
fetch('/metrics', {
method: 'POST',
body: JSON.stringify({
name,
value,
timestamp: Date.now()
})
});
}
Пример использования:
client.onConn ect = () => {
reportMetric('stomp_connected', 1);
};
При наличии realtime-приложений важно отслеживать:
Подобные данные используются:
Мониторинг бесполезен без системы оповещений.
Обычно формируются alert-правила:
Оповещения отправляются:
В микросервисной архитектуре мониторинг STOMP.js становится частью общей observability-системы.
Используются:
Пример correlation ID:
client.publish({
destination: '/queue/orders',
headers: {
'x-correlation-id': crypto.randomUUID()
},
body: JSON.stringify(order)
});
Кроме клиента необходимо отслеживать сам брокер сообщений.
Критические показатели:
Например, для RabbitMQ особенно важны:
Некоторые системы внедряют health scoring.
Пример:
function calculateHealth(metrics) {
let score = 100;
score -= metrics.errors * 5;
score -= metrics.reconnects * 2;
return Math.max(score, 0);
}
Подобные показатели помогают:
В production обычно разделяют:
Во многих проектах создаётся отдельный monitoring service.
Пример архитектуры:
STOMP Client
↓
Metrics Collector
↓
Prometheus
↓
Grafana
↓
AlertManager
Такой подход обеспечивает: