Стресс-тестирование и анализ производительности узловых проектов

Node.js внешний интерфейс GitHub V8
Стресс-тестирование и анализ производительности узловых проектов

Перед тем, как на прошлой неделе система была подключена к сети, был проведен раунд стресс-тестирования, чтобы увидеть, сколько параллелизма может выдержать система, и нагрузку в параллельных условиях. Во время стресс-теста было обнаружено, что процессор сервера был очень высоким, а 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

Обеспечивает два способа использования

  1. Командная строкаautocannon -c 100 -d 5 -p 2 http://127.0.0.1:3000/testПросто и быстро
  2. вызов APIautocannon(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Результат выглядит следующим образом

autocannon-empty

Видно, что 100 ссылок в секунду, каждая ссылка, запрос по 5 секунд, всего 31К запросов. Отчет разделен на три части: первая строка представляет собой задержку интерфейса, вторая строка представляет собой количество запросов в секунду (TPS), а третья строка представляет собой количество возвращаемых байтов в секунду. Итак, чем меньше задержка, тем выше TPS, тем лучше производительность интерфейса, т.к. Empty — это пустой интерфейс, поэтому его TPS = 6221 — это неплохо, время отклика тоже очень быстрое, мы изменили/cryptoИнтерфейс пытается

autocannon-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
аналитический отчет
  1. Для анализа только что сгенерированного файла журнала используйте официально предоставленные инструменты.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% процессорного времени.

  1. Путь файла не интуитивно понятен, затем меняем интерфейс UI, шаги следующие
  • Сначала клонируйте склад v8 внизgit clone https://github.com/v8/v8.git
  • Преобразование файлов журнала в формат jsonnode --prof-process --preprocess isolate-xxxxxxxxxx-v8.log > v8.json
  • Открытьv8/tools/profview/index.htmlФайл представляет собой статический интерфейс. Выберите только что созданный файл v8.json в центре интерфейса. После успешного анализа файла интерфейс выглядит следующим образом.

v8-ui

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

v8-ui-report

Доля узлов составляет 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);
Аналитический отчет
  1. Chrome

Наш большой Хром вот-вот выйдет.В консоли Хрома есть колонкаJavaScript ProfileКак показано ниже

chrom-cpu

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

chrom-report

Просмотрите слой за слоем, чтобы понять

  1. пламенный граф

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

flameGraph

Не говоря уже о том, как его использовать

  1. v8-analytics

Это библиотека с открытым исходным кодом, написанная лидерами сообщества.v8-analytics, официальное введение выглядит следующим образом

Анализируйте выходные данные журналов ЦП и динамической памяти с помощью таких инструментов, как v8-profiler и heapdump, и может предоставить

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

Соответствующая команда выглядит следующим образом

va test bailout --onlyЭта команда может перечислить только те функции, которые деоптимизированы движком v8.

va test timeout 200 --onlyЭта команда может перечислить только те функции, время выполнения которых превышает 200 мс.

va test leakSuspect показывает подозрительные утечки памяти в проверенном файле 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/NSW BMW/узел…

GitHub.com/Huayanjing1991/V8-…

Супер НЕТ JS.org/topic/58 это не 56...

GitHub.com/MC OL Рина/AU…

woohoo.help lib.com/GitHub/aretti…

GitHub.com/near form/no…