Перед тем, как на прошлой неделе система была подключена к сети, был проведен раунд стресс-тестирования, чтобы увидеть, сколько параллелизма может выдержать система, и нагрузку в параллельных условиях. Во время стресс-теста было обнаружено, что процессор сервера был очень высоким, а tps, время ожидания интерфейса, доступность сервисов и т. Д. Все в норме.Что, я должен был попросить помощи у босса, сказал босс сначала проверитьcpu processor
что? что это? ? Хотя я этого не понимаю, я могу проверить это ╭(╯^╰)╮, но прежде, чем я узнаю, большой парень начал это сразу, и после быстрой операции он выяснил причину~ Это действительно мне стало стыдно, мои внутренние силы далеко Это далеко не достаточно. Я вернулся, чтобы найти информацию в Интернете, и наверстать упущенное. Как проанализировать проблемы с производительностью в проекте узла
Во время процесса разработки, потому что некоторые из бизнес-логики реализованы, некоторые точки производительности могут быть проигнорированы, и эти точки могут появиться только в чуть больше, и забывайте, где видеть предложение. Разговор可能会出问题的点,便一定会出问题
Анализ проблем производительности имеет важное значение
Пример проекта
Для удобства демонстрации я написал простой небольшой пример
// app.js
const crypto = require('crypto')
const Koa = require('koa')
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/crypto', async(ctx, next) => {
const salt = crypto.randomBytes(128).toString('base64')
const hash = crypto.pbkdf2Sync('crypto', salt, 10000, 64, 'sha512').toString('hex')
ctx.body = { hash: hash }
console.log(hash)
ctx.status = 200
next()
});
let reqNum = 0
router.get('/empty', async(ctx, next) => {
ctx.body = { hash: 'empty' }
reqNum++;
ctx.status = 200
next()
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
console.log("listen 3000")
})
На основе koa2 есть два маршрута, один/crypto
, бизнес-логика заключается в использовании криптобиблиотеки для шифрования строки./empty
, интерфейс без бизнес-логики — это пустой интерфейс
испытание давлением
На рынке существует множество инструментов для стресс-тестирования, поэтому я не буду перечислять их все — я вижу, что некоторые люди рекомендуют их в сообществе.autocannon, просто дайте введение в этот инструмент, официальное введениеfast HTTP/1.1 benchmarking tool written in Node.js
, использующий инструмент стресс-тестирования, написанный узлом, может генерировать большую нагрузку, чем wrk.
install
npm i autocannon -g
npm i autocannon --save
use
Обеспечивает два способа использования
- Командная строка
autocannon -c 100 -d 5 -p 2 http://127.0.0.1:3000/test
Просто и быстро - вызов API
autocannon(opts[, cb])
Простота написания сценариев
Так много основных параметров
-
-c/--connections NUM
Количество одновременных подключений, по умолчанию 10 -
-p/--pipelining NUM
Количество конвейерных запросов на одно соединение. По умолчанию 1 -
-d/--duration SEC
время выполнения, в секундах -
-m/--method METHOD
Тип запроса по умолчанию Получить -
-b/--body BODY
тело запроса
Есть еще много параметров, вы можете проверить официальную документацию сайта.
В настоящее время эта библиотека поддерживает только один стресс-тест интерфейса. Я написал скрипт, который может поддерживать пакетное стресс-тестирование и генерировать отчеты о тестировании. Конкретный код см. в конце статьи.
report
Картинка ниже правильная/empty
Испытание на давление интерфейсаautocannon -c 100 -d 5 -p 1 http://127.0.0.1:3000/empty
Результат выглядит следующим образом
Видно, что 100 ссылок в секунду, каждая ссылка, запрос по 5 секунд, всего 31К запросов.
Отчет разделен на три части: первая строка представляет собой задержку интерфейса, вторая строка представляет собой количество запросов в секунду (TPS), а третья строка представляет собой количество возвращаемых байтов в секунду. Итак, чем меньше задержка, тем выше TPS, тем лучше производительность интерфейса, т.к. Empty — это пустой интерфейс, поэтому его TPS = 6221 — это неплохо, время отклика тоже очень быстрое, мы изменили/crypto
Интерфейс пытается
Сразу же я вижу разрыв, tps этого интерфейса составляет всего 77, а потребление времени интерфейса достигает 1100 мс, что говорит о том, что этот интерфейс имеет много возможностей для оптимизации.
Создание файлов производительности и анализ
Через инструмент нагрузочного тестирования мы нашли проблемный интерфейс.Далее нам нужно проанализировать интерфейс, но просто посмотреть на код интерфейса не так-то просто проанализировать.В конце концов, это не убедительно.Нужен отчет о производительности и используем Говоря о данных, вам будут представлены следующие два метода.
V8 Profiler
V8 официально учел это для всех, предоставивProfilerToolМетод использования также очень быстрый, шаги следующие (в качестве примера возьмем app.js)
Создать отчет
Добавьте --prof в команду запуска, напримерnode --prof app.js
, в корневом каталоге проекта будет сгенерированisolate-xxxxxxx-v8.log
Файл в формате, используемом для записи информации, такой как стек вызовов и время во время работы.Содержимое следующее (если файл большой, будет перехвачен небольшой участок вверху)
v8-version,6,1,534,47,0
shared-library,"C:\Program Files\nodejs\node.exe",0x7ff7505f0000,0x7ff751c0f000,0
shared-library,"C:\WINDOWS\SYSTEM32\ntdll.dll",0x7ff8718a0000,0x7ff871a61000,0
shared-library,"C:\WINDOWS\system32\KERNEL32.DLL",0x7ff870590000,0x7ff87063d000,0
shared-library,"C:\WINDOWS\system32\KERNELBASE.dll",0x7ff86e830000,0x7ff86ea18000,0
shared-library,"C:\WINDOWS\system32\WS2_32.dll",0x7ff86ee00000,0x7ff86ee6b000,0
аналитический отчет
- Для анализа только что сгенерированного файла журнала используйте официально предоставленные инструменты.
node --prof-process isolate-xxxxxxxx-v8.log
, результат такой (удалить бесполезную часть)
Statistical profiling result from isolate-00000209B99A60A0-v8.log, (17704 ticks, 8 unaccounted, 0 excluded).
[Shared libraries]:
ticks total nonlib name
13795 77.9% C:\WINDOWS\SYSTEM32\ntdll.dll
...
[JavaScript]:
ticks total nonlib name
12 0.1% 11.3% Builtin: CallFunction_ReceiverIsAny
...
[C++]:
ticks total nonlib name
[Summary]:
ticks total nonlib name
94 0.5% 88.7% JavaScript
0 0.0% 0.0% C++
8 0.0% 7.5% GC
17598 99.4% Shared libraries
8 0.0% Unaccounted
[C++ entry points]:
ticks cpp total name
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 1.0% are not shown.
ticks parent name
13795 77.9% C:\WINDOWS\SYSTEM32\ntdll.dll
3795 21.4% C:\Program Files\nodejs\node.exe
3768 99.3% C:\Program Files\nodejs\node.exe
3287 87.2% Function: ~pbkdf2 crypto.js:633:16
3287 100.0% Function: ~exports.pbkdf2Sync crypto.js:628:30
3287 100.0% Function: ~router.get D:\github\webapp\js\usen\app.js:8:23
3287 100.0% Function: ~dispatch D:\github\webapp\js\usen\node_modules\_koa-compose@3.2.1@koa-compose\index.js:37:23
...
Отчет состоит из шести разделов: общие библиотеки, JavaScript, C++, сводка, точки входа C++ и восходящий (тяжелый) профиль, в разделе [JavaScript] перечислены тики ЦП (такты ЦП), потребляемые выполнением кода JavaScript, и В разделе ] перечислены такты ЦП, занятые выполнением кода C++, в разделе [Сводка] указана доля каждой части, а в разделе [Внизу вверх] перечислены все функции и информация о стеке времени, занимаемого ЦП, от большего к меньшему.
согласно с3287 87.2% Function: ~pbkdf2 crypto.js:633:16
Видно, что эта функция потребляет 87,2% процессорного времени.
- Путь файла не интуитивно понятен, затем меняем интерфейс UI, шаги следующие
- Сначала клонируйте склад v8 вниз
git clone https://github.com/v8/v8.git
- Преобразование файлов журнала в формат json
node --prof-process --preprocess isolate-xxxxxxxxxx-v8.log > v8.json
- Открыть
v8/tools/profview/index.html
Файл представляет собой статический интерфейс. Выберите только что созданный файл v8.json в центре интерфейса. После успешного анализа файла интерфейс выглядит следующим образом.
Конкретные функции не будут объясняться по одному, мы расширяем слой за слоем, находим точку, занимающую много времени, и быстро находим место, которое потребляет процессор, как показано на следующем рисунке.
Доля узлов составляет 45%, из нихpbkdf2 crypto.js
занято 92%
v8-profiler
В дополнение к официальному предоставлению мы также можем выбрать библиотеку большого человека с открытым исходным кодом,v8-profiler, эта библиотека была создана относительно рано, она была создана 6 лет назад, самая последняя была полтора года назад, и оценка сообщества до сих пор хорошая
Создать отчет
Способ очень простой, маловато, придется сильно кодировать в проекте, следующим образом
profiler.startProfiling('', true);
setTimeout(function() {
var profile = profiler.stopProfiling('');
profile.export()
.pipe(fs.createWriteStream(`cpuprofile-${Date.now()}.cpuprofile`))
.on('finish', () => profile.delete())
}, 1000);
Аналитический отчет
- Chrome
Наш большой Хром вот-вот выйдет.В консоли Хрома есть колонкаJavaScript Profile
Как показано ниже
Нажмите «Загрузить», выберите только что сгенерированный файл и проанализируйте его следующим образом.
Просмотрите слой за слоем, чтобы понять
- пламенный граф
использоватьflamegraphСгенерируйте крутую пламенную диаграмму, которая отлично подойдет для репортажей.Графика на официальном сайте выглядит следующим образом.
Не говоря уже о том, как его использовать
- v8-analytics
Это библиотека с открытым исходным кодом, написанная лидерами сообщества.v8-analytics, официальное введение выглядит следующим образом
Анализируйте выходные данные журналов ЦП и динамической памяти с помощью таких инструментов, как v8-profiler и heapdump, и может предоставить
- Обратная оптимизация движка v8 или функция сбоя оптимизации помечены красным, и отображается причина сбоя оптимизации.
- Время выполнения функции превышает ожидаемое время, отмеченное красным
- Отображение подозрительных утечек памяти в текущем проекте
Соответствующая команда выглядит следующим образом
va test bailout --only
Эта команда может перечислить только те функции, которые деоптимизированы движком v8.
va test timeout 200 --only
Эта команда может перечислить только те функции, время выполнения которых превышает 200 мс.
va test leak
Suspect показывает подозрительные утечки памяти в проверенном файле heapsnapshot.
Преимущество этой библиотеки в том, что она избавляет нас от кликов и поиска по одному, что облегчает нам фильтрацию проблем~
Пакетное стресс-тестирование и формирование отчетов
Autocannon может запускать только один интерфейс. Если вы хотите протестировать следующий интерфейс, вам нужно изменить код. Например, если вы хотите протестировать несколько интерфейсов в пакетном режиме, вам нужно изменить код туда и обратно, и операция более хлопотно, поэтому я написал скрипт на основе autocannon, который можно использовать один за другим.Определенный интерфейс может быть подвергнут стресс-тестированию, а также может быть сгенерирован отчет о тестировании.
'use strict'
const autocannon = require('autocannon')
const reporter = require('autocannon-reporter')
const path = require('path')
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
/**
* @description
* 运行autocannon
* @author lizc
* @param {*} param
*/
function makeAutocannon(param) {
autocannon(param).on('done', handleResults)
}
/**
* @description
* 处理接口
* @author lizc
* @param {*} result
*/
function handleResults(result) {
const reportOutputPath = path.join(`./${result.title}_report.html`)
reporter.writeReport(reporter.buildReport(result), reportOutputPath, (err, res) => {
if (err) console.err('Error writting report: ', err)
else console.log('Report written to: ', reportOutputPath)
})
}
// 请求参数
const autocannonParam = {
url: 'http://127.0.0.1:6100/',
connections: 100,
duration: 10,
headers: {
type: 'application/x-www-form-urlencoded'
}
}
// 请求报文参数
const requestsParam = {
method: 'POST', // this should be a put for modifying secret details
headers: { // let submit some json?
'Content-type': 'application/json; charset=utf-8'
}
}
/**
* @description
* 启动批量压测
* @author lizc
* @param {*} methodList 接口列表
*/
async function run(methodList) {
const autocannonList = methodList.map(val => {
return {
...autocannonParam,
url: autocannonParam.url + val,
title: val,
requests: [
{
...requestsParam,
}
],
}
})
for (let i = 0; i < autocannonList.length; i++) {
if (i !== 0) {
await sleep((autocannonList[i - 1].duration + 2) * 1000)
makeAutocannon(autocannonList[i])
} else {
makeAutocannon(autocannonList[i])
}
}
}
// 启动
run(['order', 'crypto'])
резюме
Я портировщик для github
Вышеупомянутые методы могут в основном удовлетворить наши потребности.Конечно, производительность включает в себя множество аспектов, таких как утечки памяти, вещи и т. д. Настройка производительности — это долгий путь. Большинство статей в статье - это резюме от больших парней. Я просто делаю резюме, чтобы облегчить мое понимание и обзор. Я надеюсь, что это может помочь моим друзьям~
Ссылка на ссылку
GitHub.com/Huayanjing1991/V8-…
Супер НЕТ JS.org/topic/58 это не 56...