Heartbeat механизм

Heartbeat в STOMP — это встроенный механизм контроля «живости» соединения между клиентом и брокером сообщений, который позволяет обнаруживать разрывы канала связи, зависшие соединения и сетевые проблемы без ожидания таймаута TCP или явного разрыва WebSocket.

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


Базовая модель heartbeat в STOMP

Heartbeat в STOMP задаётся двумя параметрами:

  • cx — интервал отправки heartbeat от клиента к серверу (client → server)
  • sx — интервал ожидания heartbeat от сервера к клиенту (server → client)

Оба значения выражаются в миллисекундах.

Пример переговоров параметров heartbeat происходит во время CONNECT:

heart-beat: cx, sx

Где клиент предлагает свои значения, а брокер отвечает своими.


Формат заголовка heart-beat

В STOMP протоколе heartbeat согласуется через заголовок:

heart-beat: <cx>,<cy>
  • первая цифра — период отправки heartbeat клиентом
  • вторая цифра — допустимый интервал ожидания heartbeat от сервера

Пример:

heart-beat: 10000,10000

Это означает:

  • клиент отправляет heartbeat каждые 10 секунд
  • клиент ожидает heartbeat от сервера не реже чем раз в 10 секунд

Принцип работы на уровне протокола

После установления WebSocket-соединения STOMP-сессия переходит в фазу CONNECT/CONNECTED.

Далее:

  1. Клиент отправляет CONNECT с параметром heart-beat

  2. Сервер отвечает CONNECTED с собственными параметрами heart-beat

  3. Стороны вычисляют фактические интервалы

  4. Запускаются два независимых таймера:

    • отправка heartbeat
    • проверка получения heartbeat

Heartbeat в STOMP представляет собой одиночный символ:

\n

или байт 0x0A.


Алгоритм согласования heartbeat

STOMP.js использует стандартный алгоритм:

Фактические интервалы вычисляются как:

  • отправка клиент → server: max(cx, sy)
  • ожидание server → client: max(sx, cy)

Где:

  • cx, sx — значения клиента
  • cy, sy — значения сервера

Таким образом система гарантирует, что более «медленная» сторона определяет частоту, избегая перегрузки.


Реализация heartbeat в STOMP.js

В библиотеке @stomp/stompjs heartbeat настраивается через объект подключения:

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

heartbeatOutgoing

Определяет, как часто клиент отправляет контрольные кадры в сторону сервера.

  • значение 0 отключает отправку heartbeat
  • слишком маленькие значения могут перегрузить сеть
  • слишком большие увеличивают время обнаружения разрыва

heartbeatIncoming

Определяет, как часто клиент ожидает heartbeat от сервера.

  • при превышении интервала соединение считается неактивным
  • используется для детектирования «зависших» соединений

Внутренняя логика STOMP.js

После подключения STOMP.js запускает два независимых механизма:

1. Sender loop

Отвечает за отправку heartbeat:

  • создаётся таймер setInterval
  • с заданным интервалом отправляется \n
  • отправка происходит через WebSocket.send()

Упрощённая логика:

if (heartbeatOutgoing > 0) {
  setInterval(() => {
    ws.send("\n");
  }, heartbeatOutgoing);
}

2. Receiver watchdog

Отслеживает входящие сообщения:

  • любое сообщение (включая heartbeat) обновляет timestamp
  • отдельный таймер проверяет «давность» последнего сообщения
  • при превышении heartbeatIncoming соединение считается мёртвым

Упрощённая модель:

setInterval(() => {
  if (Date.now() - lastReceived > heartbeatIncoming) {
    disconnect();
  }
}, heartbeatIncoming);

Поведение при отсутствии heartbeat

Если одна из сторон перестаёт получать heartbeat:

  • соединение считается потерянным
  • STOMP.js инициирует disconnect
  • срабатывает callback onWebSocketClose или onStompError
  • при включённом reconnect запускается автоматическое восстановление

Связь heartbeat и WebSocket

WebSocket сам по себе не гарантирует:

  • обнаружение «зависших» соединений
  • реакцию на сетевые разрывы без закрытия TCP

Heartbeat компенсирует этот недостаток:

Механизм Функция
WebSocket ping/pong низкоуровневая проверка канала
STOMP heartbeat прикладной контроль доставки
TCP keepalive системный уровень ОС

STOMP heartbeat работает выше TCP и WebSocket, что делает его независимым от платформенных особенностей.


Серверная сторона heartbeat

Брокеры сообщений (например, RabbitMQ, ActiveMQ, Spring WebSocket STOMP) также реализуют heartbeat:

  • отправляют \n клиенту
  • ожидают heartbeat от клиента
  • закрывают соединение при таймауте

Пример поведения сервера:

  • клиент не отправляет heartbeat 30 секунд
  • сервер закрывает STOMP-сессию
  • WebSocket соединение может быть закрыто сразу или позже

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

Надёжная среда (локальная сеть)

heartbeatOutgoing: 10000
heartbeatIncoming: 10000

Интернет с нестабильной связью

heartbeatOutgoing: 5000
heartbeatIncoming: 15000

Здесь приоритет отдаётся более частой отправке и более терпимому ожиданию.

Отключение heartbeat

heartbeatOutgoing: 0
heartbeatIncoming: 0

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


Частые проблемы и особенности поведения

1. Ложные разрывы соединения

Слишком агрессивный heartbeatIncoming может привести к:

  • разрыву соединения при кратковременных задержках сети
  • нестабильным reconnect-циклам

2. Перегрузка сети

Очень малые интервалы heartbeatOutgoing:

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

3. Несовпадение настроек клиента и сервера

Если сервер не поддерживает heartbeat:

  • клиент может считать соединение мёртвым
  • требуется отключение incoming heartbeat

Поведение STOMP.js при reconnect

При включённой автоматической реконнекции heartbeat интегрирован в цикл восстановления:

  1. соединение разрывается
  2. heartbeat таймеры останавливаются
  3. выполняется reconnect
  4. заново проходит CONNECT handshake
  5. heartbeat пересоздаётся с новыми параметрами

Важно, что старые heartbeat-таймеры полностью уничтожаются при disconnect, иначе возможны утечки и дублирование отправок.


Влияние latency и jitter

Heartbeat не измеряет задержку напрямую, но косвенно зависит от:

  • RTT (round-trip time)
  • jitter сети
  • нагрузки брокера

Если latency приближается к heartbeatIncoming, возможны ложные срабатывания таймаута. Поэтому интервал должен быть существенно больше среднего RTT.


Практическая модель устойчивого heartbeat

Для стабильной работы в распределённых системах часто используется правило:

  • outgoing ≈ 1/3 от допустимого времени обнаружения разрыва
  • incoming ≈ 2–3 × RTT сети

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


Итоговая модель поведения

Heartbeat в STOMP.js формирует непрерывный цикл контроля соединения:

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

Этот механизм обеспечивает предсказуемое поведение поверх WebSocket и делает STOMP пригодным для систем реального времени, где критично быстрое обнаружение разрыва канала связи.