Если вы еще не ознакомились с первой частью диагностики в Node.js, нажмите здесь.

Это серия из трех статей о Node.js. Он основан на выступлении Колина Ирига на JSConf Colombia. Темы разделены по возрасту диагностических методик — от самых старых до самых новых:

Часть первая: переменные среды отладки, предупреждения, устаревание, выявление синхронного ввода-вывода и отклонение необработанных обещаний.

Часть вторая: отметьте Профилирование процессора, Инспектор V8, Профилирование ЦП, Снимки кучи, Асинхронные трассировки стека.

Часть третья: трассировка, трассировка TLS-соединения, покрытие кода, постмортемная отладка, диагностические отчеты.

Давайте начнем! 🚀

Отметьте Профилирование процессора

При работе с веб-приложениями мы хотим предоставить нашим пользователям максимально возможную производительность. Использование профилировщика может помочь вам определить узкие места, ведущие к вашему приложению. Это может еще больше сократить время, затрачиваемое на запрос, например на доступ к базе данных или ожидание ответа на вызов API.

Одним из таких профилировщиков является встроенный в V8 профилировщик на основе образцов. По умолчанию профилирование настроено на off, но его можно включить с помощью параметра командной строки --prof, который выводит выходные данные профилировщика V8 в файл. Сэмплер записывает стопки кода JavaScript и C/C++.

Это двухэтапный процесс: во-первых, вы можете профилировать свой код во время его выполнения. Это создаст дамп файла, который не предназначен для использования людьми: файл называется isolate-0x[numbers]-v8.log . Второй шаг берет этот вывод и форматирует его таким образом, чтобы он был удобочитаемым кодом. Это можно сделать с помощью флага --prof-process.

Файл isolate-0x[numbers]-v8.log выглядит так:

Затем вы можете запустить node --prof-process isolate-0x[numbers]-v8.log, и файл будет выглядеть так:

то есть node — prof-process isolate-0xnnnnnnnnnnnn-v8.log ›processed.txt

Здесь происходит много всего, но в основном это показывает, где вы проводите время в разделяемых библиотеках, коде JavaScript и C++.

Первая строка сообщает, что приложение использовало 761 тик для выполнения приложения. Тик подобен тактовому циклу, используемому процессом узла. Таким образом, теоретически приложение выполнялось за 761 такт. Вы также можете найти краткий раздел, в котором анализируется код JavaScript и C++.

Следует отметить, что в разделе [JavaScript] вы можете увидеть что-то под названием LazyCompile и ‘*realpathSync’. Звездочка означает, что V8 удалось оптимизировать ваш код, поэтому, если вы не видите звездочку, есть вероятность, что ваш код не оптимизирован и его выполнение занимает больше времени, чем вы думаете.

Инспектор V8

Несколько лет назад Chrome DevTools был интегрирован непосредственно в V8, что расширило его возможности за счет включения приложений Node.js. Благодаря этой интеграции можно было получить доступ к пошаговым отладчикам без установки модуля инспектора узлов.

Есть несколько способов начать работу: один использует флаг --inspect, который запускает инспектор. Затем вы можете передать хост и порт, которые вы хотите слушать, к которым вы будете подключаться позже --inspect[=[host:]port]. Если параметры не переданы, по умолчанию он будет подключаться к 127.0.0.1:9229.

Еще один способ более полезен при локальной разработке — использование флага --inspect-brk. Этот флаг имеет те же параметры для хоста и порта, что и флаг --inspect, но также ставит точку останова перед запуском пользовательского кода, поэтому вы можете выполнять любые настройки, которые вы предпочитаете, без необходимости пытаться/отлавливать точки останова в вашем коде во время выполнения.

В файле примера есть эта строка кода: Promise.reject(new Error('A very cool error here 👾'));

Теперь вызываем файл с флагом --inspect-brk

Мы видим сообщение, напечатанное в консоли: Debugger is listening on ws:, за которым следует URL-адрес веб-сокета. Websocket позволяет открыть двусторонний интерактивный сеанс связи между браузером пользователя и сервером. Мы также можем увидеть сообщение, которое направляет пользователей к документации Node.js, поэтому мы понимаем, что там происходит.

Затем, если мы перейдем по адресу chrome://inspect или еще лучше about:inspect, вы увидите что-то вроде этого:

Щелкнув по специальной ссылке DevTools для Node.js, вы увидите всплывающее окно для отладки сеанса Node.js.

Одна интересная особенность заключается в том, что когда вы уничтожаете и перезапускаете узел, окно автоматически переподключается к нему. 🔁

DevTools теперь подключен к Node.js, предоставляя вам доступ ко всем функциям Chrome DevTools, к которым вы привыкли. Это позволяет:

  • Редактируйте страницы на лету и быстро диагностируйте проблемы, что в конечном итоге поможет вам быстрее создавать лучшие веб-сайты.
  • Полная отладка точки останова, пошаговое использование черного ящика
  • Доступ к исходным картам для транспилированного кода
  • LiveEdit: оценка горячей замены JavaScript с V8
  • Оценка консоли с поддержкой функций/объектов ES6 и пользовательским форматированием объектов
  • Выборка профилировщика JavaScript с помощью flamegraph
  • Проверка моментальных снимков кучи, временная шкала выделения кучи, профилирование выделения
  • Асинхронные стеки для нативных промисов

Однако инспектор V8 никогда не должен использоваться в рабочей среде, поскольку действия DevTools останавливают событие. Это приемлемо в разработке, но не подходит для производственных сред. Если вы заинтересованы в производственной диагностике: Node.JS for Enterprise (NSolid) от NodeSource — это единственный способ получить доступ к собственным показателям и диагностике производительности и безопасности, которые не вызывают задержек в производственной среде.

Инспектор V8 очень полезен при разработке, а NSolid — в производственной среде, и вы должны попробовать его! 😉

Профилирование ЦП — в Dev и Prod

Профилирование ЦП — только в Dev

Профилирование ЦП позволяет вам понять, где существуют возможности для повышения скорости и грузоподъемности ваших процессов Node.

Одна из распространенных проблем внутри DevTools — настроить и запустить сервер, а затем попытаться запустить сеанс профилирования ЦП. Это проблематично, потому что, когда вы пытаетесь убить свой сервер и применяете load, профилирование может работать неправильно.

Чтобы решить эту проблему, флаг --cpu-prof был встроен непосредственно в Node.js. Этот флаг запустит профилировщик ЦП, и когда процесс Node.js завершится, он запишет выходные данные профиля ЦП в файл.

Вы также можете использовать флаг --cpu-prof-dir, чтобы указать каталог, в котором будет сохранен файл, и --cpu-prof-name, чтобы изменить имя файла. Если вы не укажете эти атрибуты, файл будет сохранен в вашем текущем рабочем каталоге, а имя будет представлять собой комбинацию даты, PID, TID, последовательности и будет заканчиваться ключевым словом cpuprofile.

CPU.${yyyymmdd}.${hhmmss}.${pid}.${tid}.${seq}.cpuprofile

Вы также можете установить флаг --cpu-prof-interval, указывающий, как часто профилировщик будет проверять ваше приложение. По умолчанию установлено значение в одну миллисекунду. Вы также можете использовать пользовательский интерфейс DevTools для сбора профилей вручную.

Другими словами, флаг --cpu-prof запускает профилировщик ЦП V8 при запуске и записывает профиль ЦП на диск перед выходом. Если --cpu-prof-dir не указано, профиль будет записан в текущий рабочий каталог с сгенерированным именем файла.

Вот как выглядит профиль процессора:

В верхнем разделе показано общее представление об активности ЦП с течением времени. Вы можете выбрать интервал внутри, и это покажет более подробную разбивку деятельности.

Профилирование ЦП измеряет пространство (память) или временную сложность программы, использование определенных инструкций или частоту и продолжительность вызовов функций. Чаще всего информация профилирования служит для помощи в оптимизации программы.

Профилирование ЦП — только в Prod

В производственной среде мы рекомендуем использовать NSolid. Он имеет некоторые преимущества по сравнению с Chrome Dev Tools, в том числе:

  • Его можно использовать в разработке и производстве.
  • Нет вычислительных затрат, что означает, что результаты согласуются без возникновения эффекта наблюдателя, который может исказить результаты.
  • Это упрощенная версия среды выполнения Node.js, не требующая инструментовки кода.
  • Он не останавливает цикл событий и был специально разработан для использования в производственных средах.
  • Его можно настроить для автоматического запуска профилей ЦП, если процесс превышает определенный порог производительности.

Для анализа профилей с помощью NSolid Console сначала вы запускаете консоль и выбираете интересующий процесс.

На странице сведений о процессе нажмите кнопку «Новый профиль ЦП», затем выберите окно своего профиля (от 5 до 60 секунд), желаемый стиль визуализации и профиль запуска.

Вы можете выбрать одну из трех различных визуализаций: диаграмма солнечных лучей, диаграмма пламени и древовидная карта. Следующие изображения являются примером диаграммы солнечных лучей:

Чтобы узнать больше о профилировании процессора в NSolid, посетите документацию здесь

Снимки кучи — в Dev и Prod

Снимки кучи — только в Dev

Снимок кучи — это статический снимок сведений об использовании памяти в определенный момент времени, который дает представление об использовании кучи в V8, среде выполнения JavaScript, на которой работает Node.js. Глядя на эти снимки, вы можете начать понимать, где и как используется память. Снимки кучи очень полезны для поиска и устранения проблем с памятью и производительностью в приложениях Node.js, особенно утечек памяти.

Несколько лет назад разработчикам приходилось использовать модуль heap dump для получения моментальных снимков кучи. Сегодня у нас есть встроенный сигнальный флаг моментальных снимков кучи --heapsnapshot-signal, так что вы можете отправлять столько сигналов, сколько хотите, и Node.js создаст дамп снимков кучи.

Chrome DevTools позволяет сравнивать снимки и определять объекты в памяти, что поможет сузить область возможной утечки памяти.

Вот так выглядит снимок кучи в Chrome DevTools на очень высоком уровне. Крайний левый столбец содержит список объектов в куче JavaScript.

В крайнем правом углу вы можете видеть: столбец Objects count, который показывает, сколько объектов находится в памяти, столбец shallow size, который представляет собой объем памяти, выделенный для хранения самого объекта, без учета объектов, на которые ссылаются, и столбец retained size, который это его неглубокий размер плюс неглубокие размеры объектов, которые доступны, прямо или косвенно, только из этого объекта. Другими словами, сохраняемый размер представляет собой объем памяти, который будет освобожден сборщиком мусора при сборке этого объекта.

В этом примере мы видим, что выбранный объект занимает более 3 ГБ оперативной памяти и 3 МБ памяти. Этот объект должен быть рассмотрен.

Снимки кучи — только в Prod

Лучшее решение для получения моментальных снимков кучи в продакшене — это консоль NSolid. Преимущества NSolid по сравнению с Chrome DevTools включают в себя возможность использовать его в разработке и производстве, а также предоставление живого инструментария работоспособности и стабильности вашей производственной системы без изменения кода вашего приложения и без вычислительных затрат.

Чтобы использовать моментальные снимки кучи NSolid, сначала запустите консоль и найдите список процессов справа, выберите интересующий идентификатор процесса и нажмите New Heap Snapshot в представлении сведений о процессе, как показано на изображении ниже.

Теперь, когда вы можете видеть результаты создания моментального снимка кучи, перейдите к различным объектам, проиндексированным в процессе создания моментального снимка.

Вы также можете настроить консоль NSolid для автоматического создания моментальных снимков кучи, когда какой-либо процесс превышает определенный порог производительности (например, память > 700 МБ).

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

Ячейки на левом снимке будут окрашены, отражая разницу в процентах в строке. Чем краснее ячейка, тем больше значение увеличилось по сравнению с соответствующим значением в другом снимке. Более зеленые клетки указывают на обратное. Это упростит поиск утечек памяти или проблем с производительностью, что поможет быстрее определить основную проблему.

Более подробную информацию вы можете найти здесь.

Асинхронные трассировки стека

Асинхронные трассировки стека упрощают отладку асинхронных функций. Это расширенные трассировки стека, которые включают не только текущую синхронную часть стека, но и асинхронную часть.

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

Для этого используется очень популярный модуль longjohn. Однако этот модуль сильно снижает производительность, поэтому не рекомендуется использовать его в производственной среде.

Из-за этого команда V8 добавила асинхронные трассировки стека как способ работы с асинхронным/ожидающим кодом с очень низкими затратами. Это покажет вам, где происходят асинхронные операции.

Например, здесь у нас есть функция с именем foo, которая выполняет асинхронную операцию, вызывающую функцию bar.

Обычно вы сможете видеть только полосу в трассировке стека, но с асинхронной трассировкой стека теперь вы также можете видеть foo в DevTools.

ИСПОЛЬЗОВАННАЯ ЛИТЕРАТУРА:

Профилирование кода Node.js (Часть 1: Основы)

Руководство по отладке — Node.js Docs

API веб-сокетов (WebSockets)

Отладка Node.js с помощью Chrome DevTools

Debian-узел