Как измерить и улучшить производительность Node.js

Как реализовать простую очередь задач в Node Изучение

Изменения — единственная константа в программном обеспечении, и некоторые языки меняются, как JavaScript. Одно из самых больших изменений, которые мы видели, заключается в том, что JavaScript теперь работает очень быстро.

Одним из самых быстрых существующих движков JavaScript является Google V8, который поддерживает как Chrome, так и Node.js. В простых тестах производительности хорошо оптимизированный JS (JavaScript), выполняемый V8, часто работает на 80-90% скорости соответствующего кода C ++.

Такая скорость достигается благодаря огромному объему работы, проделанной JIT-компилятором V8 (точно в срок). Компилятор JIT анализирует ваш код во время его выполнения и оптимизирует важные части вашего кода.

Итак, если JavaScript работает так быстро, почему так много приложений Node.js работают медленно?

Отчасти проблема заключается в том, что у многих разработчиков Node.js отсутствует опыт профилирования приложений. К счастью, есть отличные инструменты для профилирования наших приложений, встроенные в движки JS, которые мы используем каждый день.

В этой статье я покажу, как можно профилировать приложение Node, используя как встроенный профайлер и Raygun APM для Node.js. Я также дам несколько советов по оптимизации производительности приложений Node.js.

Использование встроенного профилировщика Node

Node имеет высококачественный профилировщик, встроенный в отладчик Node. Мы можем включить его, передав —inspectфлаг при запуске приложения.

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

Если вы хотите следовать сами, вам понадобится установленный Node.js (v12 +), а также Chrome / Chromium.

Вот код, с которого мы начнем профилировать. Сохраните его как index.jsв новой директории проекта:

const express = require('express');
const app = express();

app.get('/', slowRoute);
app.get('/fast', fastRoute);

function slowRoute(req, res) {
  loop();
  allocate();
  setTimeout(() => res.send('Success'), 100);
}

function fastRoute(req, res) {
  res.send('Success');
}

function loop() {
  for (let i = 0; i <= 1e8; i++) {}
}

function allocate() {
  const items = [];

  for (let i = 0; i < 1e6; i++) {
	items.push({ count: i });
  }
}

app.listen(3030, 'localhost', () => console.log(`Listening on localhost:3030`));

Теперь нам нужно установить экспресс через npm install express командную строку в каталоге нашего проекта.

Теперь нам нужно установить экспресс через npm install

Теперь мы можем запустить этот пример с отладчиком, запустив node —inspect index.js.

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

Теперь мы можем подключиться к сеансу отладчика, открыв Chrome, а затем открыв DevTools. Самый простой способ сделать это — щелкнуть страницу правой кнопкой мыши и выбрать «Проверить».

Теперь мы можем подключиться к сеансу отладчика

Если ваше приложение Node работает с включенным отладчиком, вы увидите значок узла слева от панели управления DevTools.

Щелчок по зеленому значку Node.js откроет окно Node.js DevTools. По умолчанию окно консоли открыто и отображает вывод вашего приложения.

Щелчок по зеленому значку Node.js откроет окно

Перейдите на вкладку «Профилировщик» и нажмите кнопку «Пуск», чтобы начать запись профиля ЦП.

Перейдите на вкладку «Профилировщик» и нажмите кнопку

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

Мы можем делать некоторые запросы к нашему веб-приложению

Откройте localhost:3030/fastв браузере или запустите curl localhost:3030/fast из командной строки, затем нажмите кнопку «Стоп» в окне Node.js DevTools.

ast из командной строки, затем нажмите кнопк

После завершения обработки профиля он откроется. Выберите представление «Диаграмма» из раскрывающегося списка в верхней части представления профиля, если оно еще не выбрано.

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

По умолчанию профилировщик показывает всю

Быстрый маршрут в нашем примере приложения отвечает только строкой «Успех». Большая часть этого профиля показывает внутренние кадры из экспресс.

При профилировании приложения это помогает составить повествование о том, что происходит в вашем коде.

Для начала мы видим onconnectionрамку. Если навести указатель мыши на него, это значит, что он взят из файла стандартной библиотеки Node net.js. Это показывает создание сокета для нового соединения.

Следующий кусок фреймов находится ниже parserOnHeadersComplete. Он включает в себя логику маршрутизации, обработку параметров и наш обработчик ( fastRoute). В последних разделах показан ответ, отправленный через сокет, за которым следует закрытие сокета.

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

Используя тот же процесс, что и раньше, мы можем захватить другой профиль. На этот раз вместо загрузки localhost:3030/fastмы загрузимся localhost:3030, что соответствует нашему медленному маршруту.

Используя тот же процесс, что и раньше, мы можем захватить

Профиль нашего медленного маршрута содержит намного больше информации. Мы можем увидеть некоторые интересные детали о том, как V8 выполняет наш код. На этой slowRouteтрассе функция занимает больше всего времени. loopФункция является первой для запуска, за которым следует allocate. Маленькие серые рамки ниже allocate- это кадры сборки мусора. Они представляют собой V8, приостанавливающий основной поток JS для очистки неиспользуемой памяти.

Может быть трудно понять, как сборщик мусора влияет на производительность наших приложений Node.js. Профилируя ключевые участки кода, мы можем видеть, когда мы сталкиваемся с задержками, вызванными сборкой мусора.

Мы также можем увидеть результат вызова setTimeoutперед отправкой ответа. После завершения allocateфункции вы можете увидеть пробел перед отправкой окончательного ответа, когда процесс Node бездействует.

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

Некоторые профилировщики работают, отслеживая данные, когда запускаются или завершаются вызовы функции / метода. Это часто называют профилировщиком уровня метода.

В отличие от этого, профилировщик выборки фиксирует стек вызовов через регулярные интервалы. Это означает, что не каждый вызов функции будет отображаться в профиле, поскольку он может выполняться слишком быстро для выборки.

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

Плюс этой конструкции — производительность. Накладные расходы профилировщика на уровне методов возрастают по мере того, как ваше приложение становится более загруженным, поскольку требуется отслеживать больше вызовов методов. Напротив, накладные расходы профилировщика выборки остаются неизменными независимо от того, насколько загружено ваше приложение. Кроме того, профилировщики выборки будут пропускать только код, который выполняется очень быстро. Всегда включены медленные функции, которые нам небезразличны при профилировании.

Совет по производительности Node.js

Создание новых объектов в JavaScript занимает память, но знаете ли вы, что это также занимает центральный процессор? Чем больше у вас объектов, тем дольше сборщик мусора тратит на их поиски. Это означает, что создание большого количества объектов может вызвать серьезные проблемы с производительностью узла. Если вам нужно создать много объектов, рассмотрите возможность использования структуры данных на основе буфера, такой как Flatbuffers или CapNProto.

Давайте сделаем еще один профиль с помощью Node DevTools. На этот раз запустите профиль, а затем localhost:3030быстро сделайте два запроса. Самый простой способ сделать это — запустить curl localhost:3030 & curl localhost:3030оболочку Unix. Вы также можете перейти localhost:3030в свой браузер и сразу же обновить его, как только он закончит загрузку.

В этом профиле мы можем видеть slowRouteзвонок по каждому

В этом профиле мы можем видеть slowRouteзвонок по каждому нашему запросу. Примечательно то, что запросы обрабатываются одновременно из-за использования setTimeout. После того, как loopи allocateшаги являются полными для первого запроса и setTimeoutназывается, второй запрос начинает процесс. После завершения второго цикла loopи allocateзвонков отправляется ответ на первый запрос.

node —inspectи DevTools Chrome — удобный способ профилировать наши локальные приложения. Но что, если мы хотим получить такую ​​информацию при тестировании / производстве? Или если мы хотим видеть больше информации, например HTTP-запросы и запросы к базе данных?

Использование профилировщика Raygun

Мы можем использовать Raygun APM для Node.js для профилирования наших приложений. Raygun APM для Node.js выводит профилировщик V8 на новый уровень, предоставляя безупречный интерфейс APM прямо из коробки. Он включает поддержку общих веб-фреймворков и адаптеров баз данных, а также многое другое.

Для захвата профилей с помощью Raygun APM для Node.js нам сначала необходимо установить агент Raygun APM.

После того, как мы установили и запустили агент, мы установим raygun-apmпакет Node.

npm i raygun-apm —save

Затем нам нужно потребовать raygun-apm/httpс самого начала нашего приложения:

require('raygun-apm/http'); // Add at the top of your application’s entrypoint
const express = require('express');
const app = express();

Мы можем запустить наше приложение node index.js, —inspectфлаг нам больше не нужен.

Теперь, когда мы делаем запрос к нашему приложению, raygun-apmмы запускаем захват профиля и отправляем его в Raygun через APM Agent.

Теперь мы можем перейти на https://app.raygun.com/, выбрать приложение, которое мы создали для тестирования, и просмотреть записанные нами следы. По умолчанию агент APM будет включать только одну трассировку каждую минуту для каждого маршрута, поэтому не все запросы приводят к появлению трассировки. Вы можете настроить это на странице выборки, которая находится на боковой панели под APM.

Теперь мы можем перейти

И теперь мы можем видеть наш захваченный след. Наиболее заметное различие, которое мы видим, заключается в том, что эта трассировка содержит гораздо меньше кадров из Express. По умолчанию raygun-apmсворачивает последовательные кадры из сторонней библиотеки в один кадр. Это помогает улучшить четкость трассировки и выделить код пользователя.

Это настраивается. Если мы установим переменную окружения RAYGUN_APM_DISABLE_STACK_SIMPLIFICATION=true, мы увидим эти кадры.

Если мы установим переменную окружения RAYGUN

Мы также можем настроить, как часто профилировщик собирает образец, что позволяет нам повысить детализацию за счет дополнительных накладных расходов.

Мы можем использовать переменную среды RAYGUN_APM_SAMPLING_FREQUENCY, значение которой по умолчанию равно 1000. Эта переменная выражается в микросекундах и соответствует одному отсчету каждую миллисекунду или тысячу раз в секунду.

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

Запрос занимает больше времени RAYGUN

Запрос занимает больше времени RAYGUN_APM_SAMPLING_FREQUENCY=1, особенно loopфункция. Это связано с накладными расходами, связанными с захватом выборки стека каждую микросекунду или миллиона выборок каждую секунду. Хотя вы не захотите часто пробовать это в производственной среде, попробовать интересно.

Темные участки вокруг отметки 800-900 мс — это не особый интерфейс, это крошечные кадры.

Этот увеличенный сегмент показывает, что allo

Этот увеличенный сегмент показывает, что allocateфункция работает в течение 307 микросекунд, после чего следует пауза GC в 102 микросекунды. Этот уровень детализации выходит за рамки того, что нам обычно нужно соблюдать при оптимизации наших приложений, поэтому по умолчанию мы используем выборки 1 мс.

raygun-apm также поддерживает сбор информации о запросах и запросах, которые выполняет ваше приложение.

Вот след из созданного мной примера приложения

Вот след из созданного мной примера приложения, которое действует как кеш для аватаров, загруженных из Gravatar. Сначала он проверит, есть ли у нас кэшированный Gravatar для данного электронного письма, а в противном случае загрузит его из Gravatar API. После получения Gravatar он сохраняется в Redis и возвращается пользователю. Желтые рамки показывают запросы базы данных к Redis, а зеленая рамка представляет запрос HTTPS к Gravatar API.

Вышеуказанная трассировка связана с промахом в кэше. Вы можете увидеть первоначальный getзапрос Redis для проверки кеша. Затем запрос на загрузку граватара с последним коротким запросом на сохранение аватара в Redis.

Для сравнения, это след от попадания в кеш

Для сравнения, это след от попадания в кеш. Этот запрос занял всего 24 мс, и мы видим, что в нем преобладает запрос Redis. Намного проще оптимизировать наши приложения, когда мы можем видеть, какие части связаны с вводом-выводом, а какие с ЦП.

raygun-apmтакже поддерживает профилирование приложений, использующих worker_threadsстандартную библиотеку Node. Здесь мы видим приведенный выше пример Gravatar, за исключением того, что теперь мы также запускаем Sharp в Worker, чтобы изменить размер Gravatar перед его сохранением в Redis. Sharp использует собственную библиотеку для изменения размера изображения, которое в настоящее время не отображается в трассировке. Мы можем видеть, где изменение размера изображения начинается и заканчивается в worker.

raygun-apm автоматически профилирует рабочих

raygun-apm автоматически профилирует рабочих по поддерживаемым версиям. Профиль запускается, когда им отправляется сообщение из кода, который профилируется, без каких-либо изменений кода.

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

raygun-apmзахватывает один профиль на запрос, максимум два за раз по умолчанию (настраивается с помощью переменной среды). Мы также собираем информацию об асинхронных контекстах приложения. Мы используем эту информацию для создания профилей, которые содержат сведения только из одного запроса.

raygun-apmзахватывает один профиль на запрос, максимум

raygun-apmзахватывает один профиль на запрос, максимум2

Эти две трассировки взяты из пары параллельных запросов. Мы видим, как они оба обрабатывают входящий запрос, запрашивают Redis и возвращают кэшированный аватар. Что обращает на себя внимание позиция (idle)и (garbage collector)кадров. Если в определенной точке профиля нет стека вызовов, это потому, что процесс Node выполнял код, не связанный с этим запросом.

Это поведение отличается от Chrome DevTools и некоторых предложений APM. Которые показывают всю активность ЦП за определенный период. Наш подход делает более понятным, что происходило с запросом, без лишнего шума.

raygun-apm также интегрируется с программным обеспечением для мониторинга ошибок Raygun.

Я изменил кешер Gravatar, чтобы сообщать об ошибках Raygun

Я изменил кешер Gravatar, чтобы сообщать об ошибках Raygun, если у нас есть какие-либо проблемы с загрузкой аватара пользователя. Здесь я смоделировал проблему с сетью, перенаправив www.gravatar.comна localhost через файл hosts моей системы. Вот почему мы видим ошибку о невозможности подключения к 127.0.0.1.

Мы можем выбрать ошибку (показанную красной точкой), а затем щелкнуть «Просмотреть в отчете о сбоях», чтобы открыть полный отчет об ошибке.

Это решение упрощает понимание того, какая

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

Raygun APM для Node.js уже доступен. Если вы уже являетесь клиентом Raygun, войдите в приложение Raygun, выберите APM на боковой панели и следуйте инструкциям по установке, чтобы начать бесплатную пробную версию.

Читайте также:  Как использовать Bootstrap с React?
Оцените статью
bestprogrammer.ru
Добавить комментарий