Мониторинг JVM, Node.js и Go-приложений. Heap, GC, event loop lag
Современные приложения редко падают внезапно. Чаще проблемы начинаются постепенно: растёт потребление памяти, увеличиваются задержки обработки запросов, появляются редкие таймауты, а пользователи начинают замечать, что сервис «стал медленнее».
При этом CPU и RAM сервера могут выглядеть вполне нормально.
Чтобы вовремя обнаруживать подобные проблемы, недостаточно мониторить только инфраструктуру. Необходимо следить за внутренним состоянием самого приложения.
Для JVM, Node.js и Go существуют свои особенности, но есть несколько ключевых метрик, которые полезно контролировать практически всегда.
Почему системных метрик недостаточно
Представим сервер с такими показателями:
- CPU — 25%;
- RAM — 60%;
- диск не перегружен;
- сеть работает стабильно.
Кажется, всё в порядке.
Однако API отвечает уже по две секунды вместо привычных двухсот миллисекунд.
Причина может находиться внутри процесса приложения:
- слишком долгие паузы сборщика мусора;
- утечка памяти;
- блокировка event loop;
- большое количество ожидающих горутин.
Такие проблемы невозможно увидеть по обычному мониторингу сервера.
Heap — сколько памяти использует приложение
Heap — это область памяти, где размещаются объекты во время работы программы.
Во всех трёх экосистемах размер heap — одна из важнейших метрик.
Сам по себе большой объём памяти не всегда означает проблему.
Гораздо важнее наблюдать за динамикой.
Например:
400 MB
520 MB
680 MB
900 MB
1.2 GBЕсли память постоянно растёт и не освобождается после работы GC, это может говорить об утечке памяти.
Подобные проблемы обычно проявляются постепенно и становятся заметны только спустя часы или даже дни работы приложения.
Garbage Collector
Большинство современных языков автоматически освобождают неиспользуемые объекты.
Этим занимается Garbage Collector.
Однако его работа тоже требует ресурсов.
Во время очистки памяти приложение может частично приостанавливать выполнение.
Если таких пауз становится слишком много, пользователи начинают ощущать рост времени ответа.
Поэтому полезно мониторить:
- количество запусков GC;
- суммарное время работы GC;
- длительность отдельных пауз;
- процент времени, которое приложение тратит на сборку мусора.
Особенно это важно для JVM, где неправильная настройка памяти способна существенно увеличить latency.
Event Loop Lag в Node.js
Node.js использует один основной поток выполнения.
Если этот поток оказывается занят тяжёлой задачей, остальные запросы вынуждены ждать.
Для оценки такой ситуации используют Event Loop Lag.
Эта метрика показывает, насколько выполнение событий отстаёт от ожидаемого времени.
Например:
Норма — 5 ms
Под нагрузкой — 250 msХотя процесс продолжает работать, пользователи уже ощущают серьёзные задержки.
Частые причины роста Event Loop Lag:
- тяжёлые вычисления;
- синхронные операции с файлами;
- блокирующие библиотеки;
- обработка слишком больших JSON-документов.
Что важно мониторить в Go
В Go нет event loop, зато есть тысячи одновременно работающих goroutines.
Обычно обращают внимание на:
- количество goroutines;
- использование heap;
- время работы GC;
- число выделений памяти;
- скорость работы планировщика.
Если число goroutines постоянно увеличивается и не уменьшается, это может свидетельствовать об их утечке.
Такая проблема постепенно приводит к росту потребления памяти и ухудшению производительности.
Что происходит при утечках памяти
Memory leak редко вызывает мгновенное падение приложения.
Гораздо чаще сценарий выглядит так:
Heap растёт
↓
GC запускается чаще
↓
Увеличиваются паузы
↓
Растёт latency
↓
OOM Killer или аварийное завершение процессаИменно поэтому отслеживание памяти позволяет обнаружить проблему задолго до сбоя.
Какие метрики стоит собирать
Для большинства приложений достаточно начать со следующего набора:
- heap usage;
- количество и время работы GC;
- event loop lag (Node.js);
- количество goroutines (Go);
- threads (JVM);
- RSS и общее потребление памяти процессом;
- время ответа приложения;
- количество активных запросов.
Практически все эти метрики приложения умеют отдавать самостоятельно.
В экосистеме JVM их обычно собирают через JMX, Micrometer или встроенные метрики Spring Boot Actuator.
Для Node.js популярны библиотеки prom-client и OpenTelemetry, которые экспортируют event loop lag, использование памяти и другие показатели.
В Go многие метрики доступны прямо из стандартной библиотеки. Чаще всего их публикуют через пакет Prometheus client_golang или собирают с помощью OpenTelemetry.
Обычно приложение отдаёт метрики по HTTP-эндпоинту (например, /metrics), после чего их регулярно считывает Prometheus. Для визуализации чаще всего используют Grafana, а для настройки уведомлений — Alertmanager.
Этого набора достаточно, чтобы увидеть большинство проблем ещё до появления пользовательских ошибок.
Какие алерты действительно полезны
Хорошие алерты обычно строятся не на абсолютных значениях, а на аномальном поведении.
Например:
- heap непрерывно растёт несколько часов;
- GC начинает занимать необычно много времени;
- event loop lag превышает допустимый порог;
- резко увеличивается число goroutines;
- приложение начинает потреблять значительно больше памяти после релиза.
Такие изменения зачастую появляются раньше, чем пользователи начинают жаловаться.
Почему полезен внешний мониторинг
Даже если внутренние метрики приложения выглядят нормально, деградация всё равно проявляется с точки зрения пользователя.
Например, из-за долгих пауз GC или блокировки event loop HTTP-запросы начинают выполняться значительно медленнее, хотя сам процесс ещё продолжает работать.
Поэтому внутренний мониторинг полезно дополнять внешним.
Например, с помощью Statuser можно контролировать доступность HTTP API, измерять время ответа из разных локаций, получать уведомления о росте latency и быстро замечать деградацию после релизов. Такой подход помогает увидеть проблему именно так, как её видят пользователи, а не только по внутренним метрикам приложения.
Итоги
Для JVM, Node.js и Go существуют разные механизмы управления памятью и выполнения кода, но основные признаки деградации во многом похожи.
Рост heap, увеличение времени работы Garbage Collector, высокий Event Loop Lag, утечки goroutines и постоянное увеличение потребления памяти — всё это ранние сигналы проблем, которые способны привести к серьёзному ухудшению производительности.
Комбинация внутренних метрик приложения и внешнего мониторинга позволяет обнаруживать подобные ситуации ещё до того, как они перерастут в инцидент и начнут влиять на пользователей.
Настроить мониторинг за 30 секунд
Надежные оповещения о даунтаймах. Без ложных срабатываний