Node использует пламенный график для оптимизации загрузки ЦП

Node.js

задний план

Без лишних слов, давайте начнем с картинки выше.Это мониторинг загрузки ЦП службы обновления статических ресурсов приложения.Видно, что со 2 по 3 июля скорость загрузки ЦП резко возросла.Уже в 8:00 Во время пика и вечернего пика в 18:00 ЦП может быть почти заполнен. Обнаружив проблему, мы решили обновить конфигурацию с 2-ядерной 4g на 4-ядерную 8g, сначала обеспечив стабильность работы сервиса, а затем продолжили исследование проблемы.

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

图一

анализ проблемы

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

график пламени

Итак, как сгенерировать пламенный граф, стало моей самой большой проблемой.Я начал поиск в Google, "как генерировать пламенный граф", "граф пламенных узлов", "профилировщик узловых процессоров", "граф пламенных узлов". Вроде все статьи одинаковые, и более 95% статей - это следующие решения.

1.Linux perf

Справочная статья:Руководство по отладке Nodejs perf + FlameGraph

Инструмент анализа производительности системы, идущий в комплекте с линуксом, про кучу функций больше рассказывать не буду, если интересно, можете посмотреть самируководство по отладке nodejsОткройте первую страницу книги. Поскольку ограничение использования не Linux, первый шаг apt install linux-tools-common небезопасен, если слишком сложно работать на виртуальной машине или что-то в этом роде, план мертв.

2. Инструменты анализа, поставляемые с Node.js

Справочная статья:Приложения Node.js, которые легко анализировать | Node.js

Начиная с Node.js 4.4.0, нода сама может записывать информацию о производительности (профайлер) движка V8 в процессе, достаточно добавить параметр --prof при запуске. Инструменты анализа, поставляемые с Node:

  1. При запуске приложения node необходимо вывести параметр --prof
  2. Затем информация, связанная с производительностью, будет собрана в рабочем каталоге узла для создания файла isolate-xxxxxxxxxxxx-v8.log.
  3. В npm есть пакет, который может легко преобразовать изолированный файл непосредственно в пламенный график в формате html.GitHub — mapbox/flamebearer: инструмент для работы с графами Blazing Fast Flame для V8 и Node 🔥После выполнения вышеуказанных шагов график пламени закончился, как и ожидалось.
    图二
    Но при ближайшем рассмотрении это кажется не так, потому что проект использует фреймворк egg, а вся информация во флэм-графе — это что-то, начатое яйцом, мой пятиминутный стресс-тест интерфейса не отразился на плам-графе на все.на, погладил себя по голове, вспомнил, что данные о производительности, которые я собирал в виде node --prof, были все дела на основном процессе яйца, и все наши интерфейсы все попали в яйцо воркер, и ничего не было собрано . Кстати, яйцо обеспечивает режим одиночного процессаRFC добавляет режим однопроцессорного запуска · Проблема № 3180 · eggjs/egg · GitHubНо пока это только в экспериментальной стадии. Второй план опять умер, но к счастью я увидел хотя бы картинку.

3. Используйте Dtrace для сбора данных о производительности

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

图三
Это все мелочи в низу v8, и не похоже, что есть что-то, что я хочу видеть.План третий.

Ну и выше различные решения которые я придумал от гугла.После того как я наступил на яму одно за другим,все они закончились провалом.На самом деле есть и более простые решения,такие как прямое подключение к алиноду и использование Платформа Alibaba Cloud. С одной стороны, этот проект Без доступа к Alibaba Cloud образ узла, который я только что использовал, не является Ali. С другой стороны, если проблема может быть обнаружена в среде разработки, я не хочу идти онлайн, чтобы проверить проблему.

Вариант 4: v8-профилировщик

Node.js основан на движке V8, V8 предоставляет некоторый API-интерфейс профилировщика, мы можем передатьv8-profilerСоберите некоторые данные ЦП и памяти во время выполнения. Я столкнулся с некоторыми проблемами при установке v8-profiler, и они всегда не удавались и не могли быть решены. Но, к счастью, великий бог выпустил v8-profiler-node8 на основе v8-profiler.Ниже приводится описание v8-profiler-node8.

Based on v8-profiler-node8@5.7.0, Solved the v8-profiler segment fault error in node-v8.x.
v8-profiler-node8 provides node bindings for the v8 profiler and integration with node-inspector
Сбор данных: После простой установки npm v8-profiler-node8 начните собирать профили ЦП в течение пяти минут.

const profiler = require('v8-profiler-node8');
const fs = require('fs');
const Bluebird = require('bluebird');

class PackageController extends Controller {
  async cpuProf() {
    console.log('开始收集');
    // Start Profiling
    profiler.startProfiling('CPU profile');
    await Bluebird.delay(60000 * 5);
    const profile = profiler.stopProfiling();
    profile.export()
      .pipe(fs.createWriteStream(`cpuprofile-${Date.now()}.cpuprofile`))
      .on('finish', () => profile.delete());
    this.ctx.status = 204;
  }
}

Затем немедленно используйте испытание под давлением, чтобы оказать давление на службу,

ab -t 300 -c 10 -p post.txt -T "application/json" http://localhost:7001/xxx/xxxxx/xxxxxx/xxxxx

После завершения сбора получается файл cpuProfile.Chrome поставляется с инструментом для анализа журнала профиля ЦП. Откройте Chrome -> откройте инструменты разработчика (DevTools) -> нажмите кнопку с тремя точками в правом верхнем углу -> Дополнительные инструменты -> Профилировщик JavaScript -> Загрузить, загрузите только что созданный файл cpuprofile. Вы можете напрямую прочитать этот файл и открыть анализ с помощью анализа производительности Chrome. Вот я бы порекомендовалspeedscopeИнструмент, который генерирует графики пламени на основе cpuProfile, генерируемые им графики пламени более четкие, и есть режим leftHeavy, который напрямую ранжирует тот, у кого самая высокая загрузка ЦП, в крайнем левом углу, что ясно с первого взгляда и может быстро найти проблема.

图四

Решение задач на основе графиков пламени

Ниже показан левый тяжелый режим графика пламени.

图五

Глядя на график пламени, чем четче график, тем он более нормальный. Чем длиннее горизонтальная полоса, тем больше время работы. Из графика видно, что ЦП занимал до 2 минут в течение пяти минут. стресс-теста, и большинство из них были красными. Займите коробку, сделайте большую фотографию

图六
Этот график пламени представляет собой последовательность вызовов сверху вниз. На первый взгляд, в моем бизнес-коде нет содержимого. Если вы внимательно посмотрите, fetchDocs, Cursor.next, completeMany и Document.init кажутся монго-вещами, счастливыми. дурак, поторопитесь и поищите исходный код. Случай решается из completeMany, Это метод в мангусте, Функция состоит в том, чтобы упаковать результат запроса, чтобы каждый документ в результате стал документом мангуста, чтобы он мог продолжать использовать методы, предоставляемые мангустом. Соответствующий исходный код выглядит следующим образом.

/*!
 * hydrates many documents
 *
 * @param {Model} model
 * @param {Array} docs
 * @param {Object} fields
 * @param {Query} self
 * @param {Array} [pop] array of paths used in population
 * @param {Function} callback
 */
function completeMany(model, docs, fields, userProvidedFields, pop, callback) {
  var arr = [];
  var count = docs.length;
  var len = count;
  var opts = pop ? { populated: pop } : undefined;
  var error = null;
  function init(_error) {
    if (error != null) {
      return;
    }
    if (_error != null) {
      error = _error;
      return callback(error);
    }
    --count || callback(null, arr);
  }
  for (var i = 0; i < len; ++i) {
    arr[i] = helpers.createModel(model, docs[i], fields, userProvidedFields);
    arr[i].init(docs[i], opts, init);
  }
}

Метод completeMany превратит каждый входящий документ в документ мангуста через helpers.createModel. Давайте посмотрим, где вызывается метод completeMany, и обнаружим, что в методе find он будет судить, равен ли options.lean true или нет. , Чтобы вызвать метод completeMany для переноса результата запроса.

/**
 * Thunk around find()
 *
 * @param {Function} [callback]
 * @return {Query} this
 * @api private
 */
Query.prototype._find = function(callback) {
  this._castConditions();
  if (this.error() != null) {
    callback(this.error());
    return this;
  }
  this._applyPaths();
  this._fields = this._castFields(this._fields);
  var fields = this._fieldsForExec();
  var options = this._mongooseOptions;
  var _this = this;
  var userProvidedFields = _this._userProvidedFields || {};
  var cb = function(err, docs) {
    if (err) {
      return callback(err);
    }
    if (docs.length === 0) {
      return callback(null, docs);
    }
    if (!options.populate) {
		// 看这里 重点重点!
      return !!options.lean === true
          ? callback(null, docs)
          : completeMany(_this.model, docs, fields, userProvidedFields, null, callback);
    }
    var pop = helpers.preparePopulationOptionsMQ(_this, options);
    pop.__noPromise = true;
    _this.model.populate(docs, pop, function(err, docs) {
      if (err) return callback(err);
      return !!options.lean === true
          ? callback(null, docs)
          : completeMany(_this.model, docs, fields, userProvidedFields, pop, callback);
    });
  };
  return Query.base.find.call(this, {}, cb);
};

Перейдите к документации и найдите бережливыйmongoose query leanВ документации говорится, что если используется Lean, то запрос будет возвращать объекты javascript, а неMongoose Documents. Оригинальные слова следующие.

Documents returned from queries with theleanoption enabled are plain javascript objects, not Mongoose Documents . They have nosavemethod, getters/setters, virtuals, or other Mongoose features.

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

оптимизация

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

await model.Package.find(query).lean();

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

图七
图八

Из флейм-графика на рисунке 1 нет очевидной разницы, но как только вы видите рисунок 2, вы знаете, что наша оптимизация эффективна.Из самого интуитивного, часть completeMany в красном поле слева отсутствует напрямую , Затем общее время, занимаемое ЦП, также напрямую уменьшается с исходных почти двух минут до 36 с, и эффект оптимизации все еще очень очевиден. Выйдите в интернет на несколько дней, чтобы увидеть эффект

图9
Как видно из рисунка, после оптимизации загрузка процессора значительно улучшилась и стабилизировалась в пределах 15%. Проблема решена, все устраивает, сервер понижен и все в норме. Но этот сбой также заставил меня быть более осторожным при использовании ODM, таких как mongoos.Хотя это приносит нам бесконечное удобство, это также может создать дополнительную нагрузку на наши службы из-за некоторых дополнительных операций.В нормальных условиях этот разрыв в производительности не легко обнаружить.Однако в пиковые периоды или масштабные события, эта небольшая яма может иметь большее влияние на обслуживание.

Помните.

Колонка автора:Талант /user/301671…