Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

От SocialCalc к EtherCalc

Глава 2 из книги "Производительность приложений с открытым исходным кодом".

Оригинал: From SocialCalc to EtherCalc
Автор: Audrey Tang
Перевод: А.Панин

Профилирование Node.js

Для установления операции, на выполнение которой тратятся циклы центрального процессора, нам был нужен профайлер.

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

В отличие от описанных инструментов, инструменты для профилирования Node.js оставляют желать лучшего. На момент написания этой главы сфера применения кода для поддержки DTrace ограничена системами на основе Illumos, работающими в 32-битном режиме, поэтому мы в большей степени использовали инструмент Node Webkit Agent, который предоставлял доступный интерфейс профилирования, хотя и выводил исключительно статистику в масштабах функций.

Типичная сессия профилирования выглядит следующим образом:
# "lsc" is the LiveScript compiler
# Load WebKit agent, then run app.js:
lsc -r webkit-devtools-agent -er ./app.js
# In another terminal tab, launch the profiler:
killall -USR2 node
# Open this URL in a WebKit browser to start profiling:
open http://tinyurl.com/node0-8-agent

Для воссоздания высокой фоновой нагрузки мы начали отправлять множество одновременных запросов посредством API REST с помощью инструмента тестирования производительности Apache с названием ab. Для симуляции операций со стороны браузера, таких, как перемещение курсоров или обновление формул, мы использовали Zombie.js, а эмулятор браузера был также создан с использованием программных компонентов jsdom и Node.js.

По иронии, узкое место обнаружилось в самом программном компоненте jsdom.

Снимок окна профайлера (при работе с jsdom)
Рисунок 2.5 - Снимок окна профайлера (при работе с jsdom)

В отчете, представленном на Рисунке 2.5, вы можете увидеть, что функция RenderSheet использует большую часть времени центрального процессора. Каждый раз, когда сервер получает команду, он тратит несколько микросекунд на перерисовку содержимого ячеек, устанавливаемого с использованием свойства innerHTML для вывода результата исполнения каждой из команд.

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

В ходе тщательного исследования методов использования памяти из кучи, мы обнаружили, что на результаты выполнения операций практически не устанавливается ссылок, так как на самом деле нам не требуется хранить обновляемые в реальном времени данные таблицы в формате HTML на стороне сервера. Единственной ссылкой на эти данные является функция из API для экспорта данных в формате HTML, причем для реализации этой функции мы также можем реконструировать содержимое каждой ячейки в формате HTML (представляемое свойством innerHTML) на основе структуры электронной таблицы, хранящейся в памяти.

Исходя из этого, мы удалили вызов jsdom из функции RenderSheet, повторно реализовали минимальный вариант системы для работы с объектной моделью документа DOM в рамках 20 строк на языке LiveScript с целью предоставления поддержки экспорта данных в формат HTML, после чего снова запустили профайлер (обратитесь к Рисунку 2.6).

Обновленный снимок окна профайлера (при работе без jsdom)
Рисунок 2.6 - Обновленный снимок окна профайлера (при работе без jsdom)

Гораздо лучше! Мы улучшили пропускную способность системы в 4 раза, причем функция экспорта в формат HTML стала работать в 20 раз быстрее и проблема прекращения работы серверного приложения исчезла сама собой.


Продолжение статьи: Масштабирование на многоядерных системах.