Мониторинг состояния системы

Приложения, использующие WebSocket-соединения и брокеры сообщений, работают в условиях постоянного обмена событиями. При высокой нагрузке ошибки могут накапливаться незаметно: соединения начинают разрываться, сообщения теряются, задержки увеличиваются, а клиенты уходят в бесконечные переподключения. Без системы мониторинга подобные проблемы становятся заметны только после жалоб пользователей.

STOMP.js предоставляет механизмы, позволяющие отслеживать:

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

Мониторинг строится как на уровне клиента 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'];
};

Подобная структура может использоваться:

  • в React/Vue-хранилищах;
  • в системах телеметрии;
  • в административных панелях;
  • в DevTools-инструментах;
  • в пользовательских индикаторах подключения.

Мониторинг heartbeat-сигналов

Heartbeat — механизм проверки активности соединения между клиентом и брокером.

Настройка heartbeat:

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

Здесь:

  • heartbeatIncoming — ожидание heartbeat от сервера;
  • heartbeatOutgoing — отправка heartbeat серверу.

Если 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);

Такой механизм особенно полезен:

  • при нестабильных сетях;
  • в мобильных приложениях;
  • при работе через reverse proxy;
  • при использовании облачных балансировщиков.

Логирование сетевой активности

STOMP.js поддерживает встроенное логирование.

client.debug = (message) => {
    console.log('[STOMP]', message);
};

В логах появляются:

  • CONNECT;
  • CONNECTED;
  • SEND;
  • SUBSCRIBE;
  • MESSAGE;
  • ERROR;
  • DISCONNECT;
  • heartbeat-события.

Пример вывода:

[STOMP] Opening Web Socket...
[STOMP] Web Socket Opened...
[STOMP] >>> CONNECT
accept-version:1.2
heart-beat:10000,10000

Интеграция с системами логирования

В production-среде console.log недостаточен.

Логи обычно передаются:

  • в Elasticsearch;
  • в Grafana Loki;
  • в Datadog;
  • в Splunk;
  • в Sentry;
  • в New Relic.

Пример:

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);
};

Дополнительно можно собирать:

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

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

Иногда соединение устанавливается слишком долго.

const startedAt = Date.now();

client.onConn ect = () => {
    const duration = Date.now() - startedAt;

    console.log('Connection time:', duration);
};

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

  • перегрузку брокера;
  • DNS-проблемы;
  • сетевые задержки;
  • медленный TLS-handshake;
  • ограничения прокси-сервера.

Измерение задержки доставки сообщений

Одним из важнейших показателей является 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);
});

Мониторинг очередей сообщений

На стороне брокера важно отслеживать:

  • размер очередей;
  • количество потребителей;
  • скорость доставки;
  • число неподтверждённых сообщений;
  • накопление backlog.

Например, в RabbitMQ доступны:

  • queue depth;
  • message rates;
  • consumer utilization;
  • acknowledgements;
  • memory usage.

Мониторинг подтверждений ACK

При использовании ручного подтверждения сообщений необходимо отслеживать 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 обычно анализируются:

  • тип ошибки;
  • stack trace;
  • время возникновения;
  • частота повторения;
  • повреждённые payload.

Метрики производительности подписок

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

Пример измерения времени обработки:

client.subscribe('/topic/events', (message) => {

    const started = performance.now();

    handleEvent(message.body);

    const ended = performance.now();

    console.log('Processing:', ended - started);
});

Полезные показатели:

  • среднее время обработки;
  • максимальная задержка;
  • p95/p99 latency;
  • throughput;
  • messages per second.

Наблюдение за размером сообщений

Слишком большие payload могут вызывать:

  • рост latency;
  • проблемы сериализации;
  • превышение лимитов брокера;
  • рост использования памяти.

Мониторинг размера:

client.subscribe('/topic/data', (message) => {

    const size = new Blob([message.body]).size;

    console.log('Message size:', size);
});

Мониторинг использования памяти

При долгоживущих WebSocket-соединениях возможно накопление утечек памяти.

Проблемы возникают из-за:

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

Пример опасного кода:

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);
}

Отслеживаются:

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

Контроль утечек подписок

Ошибка:

setInterval(() => {

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

}, 1000);

Каждую секунду создаётся новая подписка.

Правильный вариант:

let subscription = null;

if (!subscription) {
    subscription = client.subscribe('/topic/test', handler);
}

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

Популярный подход — экспорт метрик в 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

Grafana позволяет визуализировать:

  • количество сообщений;
  • активные соединения;
  • задержки;
  • ошибки;
  • reconnect rate;
  • queue backlog.

Типичные панели:

  • latency heatmap;
  • reconnect timeline;
  • throughput chart;
  • active consumers;
  • error ratio.

Мониторинг браузерных WebSocket-соединений

В браузере доступны инструменты разработчика.

Во вкладке Network → WS можно анализировать:

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

Это позволяет быстро выявлять:

  • дубли сообщений;
  • циклические reconnect;
  • избыточный трафик;
  • проблемы сериализации.

Сбор пользовательской телеметрии

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

function reportMetric(name, value) {

    fetch('/metrics', {
        method: 'POST',
        body: JSON.stringify({
            name,
            value,
            timestamp: Date.now()
        })
    });
}

Пример использования:

client.onConn ect = () => {
    reportMetric('stomp_connected', 1);
};

Мониторинг активности пользователей

При наличии realtime-приложений важно отслеживать:

  • количество активных клиентов;
  • время онлайн;
  • частоту сообщений;
  • активность каналов;
  • географию соединений.

Подобные данные используются:

  • для балансировки нагрузки;
  • для autoscaling;
  • для capacity planning;
  • для выявления аномалий.

Алертинг и уведомления

Мониторинг бесполезен без системы оповещений.

Обычно формируются alert-правила:

  • reconnect rate > 20%;
  • latency > 2 секунд;
  • queue depth > 100000;
  • heartbeat timeout;
  • broker unavailable;
  • memory usage > 90%.

Оповещения отправляются:

  • в Slack;
  • в Telegram;
  • по email;
  • через PagerDuty;
  • через Opsgenie.

Наблюдаемость распределённых систем

В микросервисной архитектуре мониторинг STOMP.js становится частью общей observability-системы.

Используются:

  • tracing;
  • centralized logging;
  • metrics aggregation;
  • correlation ID;
  • distributed monitoring.

Пример correlation ID:

client.publish({
    destination: '/queue/orders',
    headers: {
        'x-correlation-id': crypto.randomUUID()
    },
    body: JSON.stringify(order)
});

Мониторинг состояния брокера

Кроме клиента необходимо отслеживать сам брокер сообщений.

Критические показатели:

  • CPU usage;
  • RAM usage;
  • file descriptors;
  • disk I/O;
  • network throughput;
  • connection count;
  • queue saturation.

Например, для RabbitMQ особенно важны:

  • Erlang VM memory;
  • channels count;
  • socket usage;
  • queue mirroring status.

Автоматическое обнаружение деградации

Некоторые системы внедряют health scoring.

Пример:

function calculateHealth(metrics) {

    let score = 100;

    score -= metrics.errors * 5;
    score -= metrics.reconnects * 2;

    return Math.max(score, 0);
}

Подобные показатели помогают:

  • автоматически отключать проблемные ноды;
  • запускать failover;
  • переключать traffic routing;
  • инициировать autoscaling.

Мониторинг в production-среде

В production обычно разделяют:

Технические метрики

  • CPU;
  • RAM;
  • reconnect;
  • queue depth;
  • network latency.

Бизнес-метрики

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

Метрики пользовательского опыта

  • скорость обновления интерфейса;
  • время появления уведомлений;
  • задержка чата;
  • стабильность соединения.

Централизованный сервис мониторинга

Во многих проектах создаётся отдельный monitoring service.

Пример архитектуры:

STOMP Client
    ↓
Metrics Collector
    ↓
Prometheus
    ↓
Grafana
    ↓
AlertManager

Такой подход обеспечивает:

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