portaldacalheta.pt
  • Основен
  • Растеж На Приходите
  • Финансови Процеси
  • Дизайнерски Живот
  • Съвети И Инструменти
Back-End

Отстраняване на грешки в паметта в приложенията Node.js



Веднъж карах Audi с V8 двойно-турбо двигател вътре и неговото представяне беше невероятно. Карах около 140MPH по магистрала IL-80 близо до Чикаго в 3 часа сутринта, когато нямаше никой на пътя. Оттогава терминът „V8“ за мен се свързва с висока производителност.

Node.js е платформа, изградена върху JavaScript двигателя на V8 на Chrome за лесно изграждане на бързи и мащабируеми мрежови приложения.

Въпреки че V8 на Audi е много мощен, вие все още сте ограничени с капацитета на вашия резервоар за газ. Същото важи и за V8 на Google - двигателят на JavaScript зад Node.js. Неговата производителност е невероятна и има много причини, поради които Node.js работи добре за много случаи на употреба , но винаги сте ограничени от размера на купчината. Когато трябва да обработите повече заявки във вашето приложение Node.js, имате два варианта: или вертикално, или хоризонтално. Хоризонталното мащабиране означава, че трябва да стартирате по-едновременни екземпляри на приложения. Когато свършите правилно, в крайна сметка можете да обслужвате повече заявки. Вертикалното мащабиране означава, че трябва да подобрите използването и производителността на паметта на приложението си или да увеличите наличните ресурси за вашето приложение.



Отстраняване на грешки в паметта в приложенията Node.js



Отстраняване на грешки в паметта в приложенията Node.js Tweet

Наскоро бях помолен да работя върху приложение Node.js за един от моите клиенти на ApeeScape, за да поправя проблем с изтичане на памет. Приложението, API сървър, беше предназначено да може да обработва стотици хиляди заявки всяка минута. Оригиналното приложение заемаше почти 600 MB RAM и затова решихме да вземем горещите крайни точки на API и да ги реализираме отново. Режийните стават много скъпи, когато трябва да обслужвате много заявки.



За новия API избрахме restify с естествен драйвер MongoDB и Kue за фонови задачи. Звучи като много лек стек, нали? Не точно. По време на пиково натоварване нов екземпляр на приложение може да консумира до 270MB RAM. Следователно мечтата ми да има два случая на приложение за 1X Heroku Dyno изчезна.

Node.js Арсенал за отстраняване на грешки в паметта

Memwatch

Ако търсите „как да намерите теч в възел“, първият инструмент, който вероятно ще намерите, е memwatch . Оригиналният пакет беше изоставен отдавна и вече не се поддържа. Можете обаче лесно да намерите по-нови версии от него в GitHub’s списък с вилици за хранилището . Този модул е ​​полезен, защото може да излъчва събития за изтичане, ако види, че купчината нараства над 5 последователни колекции от боклук.



Heapdump

Страхотен инструмент, който позволява Разработчици на Node.js за да направите снимка на купчина и да ги инспектирате по-късно с Chrome Developer Tools.

Инспектор на възел

Дори по-полезна алтернатива на heapdump, тъй като ви позволява да се свържете с работещо приложение, да вземете heap dump и дори да отстранявате грешки и да го рекомпилирате в движение.



Вземане на “node-inspector” за завъртане

За съжаление няма да можете да се свържете с производствени приложения, които се изпълняват на Heroku, тъй като не позволяват изпращането на сигнали към работещи процеси. Heroku обаче не е единствената хостинг платформа.

За да изпитаме node-inspector в действие, ще напишем просто приложение Node.js, използвайки restify и ще добавим малко източник на изтичане на памет в него. Всички експерименти тук се правят с Node.js v0.12.7, който е компилиран срещу V8 v3.28.71.19.



var restify = require('restify'); var server = restify.createServer(); var tasks = []; server.pre(function(req, res, next) { tasks.push(function() { return req.headers; }); // Synchronously get user from session, maybe jwt token req.user = { id: 1, username: 'Leaky Master', }; return next(); }); server.get('/', function(req, res, next) { res.send('Hi ' + req.user.username); return next(); }); server.listen(3000, function() { console.log('%s listening at %s', server.name, server.url); });

Приложението тук е много просто и има много очевиден теч. Масивът задачи ще расте през целия живот на приложението, което ще го забави и в крайна сметка ще се срине. Проблемът е, че не само изтичаме затваряне, но и цели обекти на заявки.

GC във V8 използва стратегия за спиране на света, следователно означава повече обекти, които имате в паметта, колкото по-дълго ще отнеме събирането на боклука. В дневника по-долу можете ясно да видите, че в началото на живота на приложението ще отнеме средно 20 ms за събиране на боклука, но няколкостотин хиляди заявки по-късно отнема около 230 ms. Хората, които се опитват да получат достъп до нашето приложение, ще трябва да изчакат 230ms по-дълго сега заради GC. Също така можете да видите, че GC се извиква на всеки няколко секунди, което означава, че на всеки няколко секунди потребителите ще имат проблеми с достъпа до нашето приложение. И забавянето ще нарасне, докато приложението се срине.



[28093] 7644 ms: Mark-sweep 10.9 (48.5) -> 10.9 (48.5) MB, 25.0 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 7717 ms: Mark-sweep 10.9 (48.5) -> 10.9 (48.5) MB, 18.0 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 7866 ms: Mark-sweep 11.0 (48.5) -> 10.9 (48.5) MB, 23.2 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 8001 ms: Mark-sweep 11.0 (48.5) -> 10.9 (48.5) MB, 18.4 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. ... [28093] 633891 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 357.3 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 635672 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 331.5 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 637508 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 357.2 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested].

Тези дневници се отпечатват, когато приложението Node.js се стартира с –Trace_gc флаг:

node --trace_gc app.js

Нека приемем, че вече сме стартирали нашето приложение Node.js с този флаг. Преди да свържем приложението с node-inspector, трябва да му изпратим сигнала SIGUSR1 към текущия процес. Ако стартирате Node.js в клъстера, не забравяйте да се свържете с един от подчинените процеси.



kill -SIGUSR1 $pid # Replace $pid with the actual process ID

Правейки това, ние караме приложението Node.js (по-точно V8) да влезе в режим за отстраняване на грешки. В този режим приложението автоматично отваря порт 5858 с V8 протокол за отстраняване на грешки .

Следващата ни стъпка е да стартираме node-inspector, който ще се свърже с интерфейса за отстраняване на грешки на работещото приложение и ще отвори друг уеб интерфейс на порт 8080.

$ node-inspector Node Inspector v0.12.2 Visit http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858 to start debugging.

В случай, че приложението работи в работна версия и имате инсталирана защитна стена, ние можем да тунелираме отдалечен порт 8080 към localhost:

ssh -L 8080:localhost:8080 [email protected]

Сега можете да отворите уеб браузъра си Chrome и да получите пълен достъп до инструментите за разработка на Chrome, прикачени към вашето отдалечено производствено приложение. За съжаление, Chrome Developer Tools няма да работи в други браузъри.

Нека намерим теч!

Изтичането на памет във V8 не е истинско изтичане на памет, както ги познаваме от приложенията C / C ++. В JavaScript променливите не изчезват в празнотата, те просто се „забравят“. Нашата цел е да намерим тези забравени променливи и да им напомним, че Доби е свободен.

Вътре в Chrome Developer Tools имаме достъп до множество профилиращи. Ние сме особено заинтересовани от Запис на разпределението на купчината който се изпълнява и прави множество снимки на куп с течение на времето. Това ни дава ясен поглед кои предмети изтичат.

Започнете да записвате разпределения на купчина и нека симулираме 50 едновременни потребители на нашата начална страница, използвайки Apache Benchmark.

Екранна снимка

ab -c 50 -n 1000000 -k http://example.com/

Преди да правите нови моментни снимки, V8 ще извърши събиране на боклука, така че определено знаем, че в моментната снимка няма стари боклуци.

Поправяне на теча на мухата

След събиране на моментни снимки за разпределение на купчина за период от 3 минути завършваме с нещо като следното:

Екранна снимка

Ясно виждаме, че в купчина има и гигантски масиви, много обекти IncomingMessage, ReadableState, ServerResponse и Domain. Нека се опитаме да анализираме източника на теча.

При избора на куп разминаване на диаграмата от 20s до 40s, ще видим само обекти, които са добавени след 20s от момента, в който сте стартирали профила. По този начин можете да изключите всички нормални данни.

Отбелязвайки колко обекта от всеки тип са в системата, ние разширяваме филтъра от 20s до 1min. Виждаме, че масивите, които вече са доста гигантски, продължават да растат. Под „(масив)“ можем да видим, че има много обекти „(свойства на обекта)“ с еднакво разстояние. Тези обекти са източникът на изтичането на паметта ни.

Също така можем да видим, че „(затваряне)“ обектите също растат бързо.

Може да е удобно да погледнете и струните. Под списъка със струни има много фрази „Hi Leaky Master“. Това също може да ни даде някаква подсказка.

В нашия случай знаем, че низът „Hi Leaky Master“ може да бъде сглобен само по маршрута „GET /“.

Ако отворите пътя на фиксаторите, ще видите, че този низ по някакъв начин се препраща към изискване , тогава има създаден контекст и всичко това добавено към някакъв гигантски набор от затваряния.

Екранна снимка

В този момент знаем, че имаме някакъв гигантски набор от затваряния. Нека да отидем и да дадем име на всички наши затваряния в реално време в раздела източници.

Екранна снимка

След като приключим с редактирането на кода, можем да натиснем CTRL + S, за да запазим и прекомпилираме кода в движение!

Сега нека запишем друг Снимка на разпределението на купчина и вижте кои затваряния заемат паметта.

Ясно е, че SomeKindOfClojure () е нашият злодей. Сега можем да видим това SomeKindOfClojure () затварянията се добавят към някакъв масив с име задачи в глобалното пространство.

Лесно е да се види, че този масив е просто безполезен. Можем да го коментираме. Но как да освободим паметта на вече заетата памет? Много лесно, просто присвояваме празен масив на задачи и със следващата заявка тя ще бъде отменена и паметта ще бъде освободена след следващото GC събитие.

Екранна снимка

Доби е безплатен!

Животът на боклука във V8

Е, V8 JS няма изтичане на памет, а само забравени променливи.

Е, V8 JS няма изтичане на памет, а само забравени променливи. Tweet

Купчината V8 е разделена на няколко различни пространства:

  • Ново пространство : Това пространство е относително малко и има размер между 1MB и 8MB. Повечето от обектите са разпределени тук.
  • Пространство на стария указател : Има обекти, които могат да имат указатели към други обекти. Ако обектът оцелее достатъчно дълго в Новото Космос, той се повишава в Старото указателно пространство.
  • Старо пространство за данни : Съдържа само сурови данни като низове, числа в кутии и масиви от двойни кутии. Обектите, оцелели достатъчно дълго в Новото пространство, също се преместват тук.
  • Голямо обектно пространство : В това пространство се създават обекти, които са твърде големи, за да се поберат в други пространства. Всеки обект има свой собствен mmap ‘ed регион в паметта
  • Кодово пространство : Съдържа сглобен код, генериран от JIT компилатора.
  • Клетъчно пространство, пространство на свойство, пространство на картата : Това пространство съдържа Cell s, PropertyCell s и Map s. Това се използва за опростяване на събирането на боклука.

Всяко пространство е съставено от страници. Страницата е регион памет, разпределен от операционната система с mmap. Всяка страница винаги е с размер 1MB, с изключение на страници в голямо обектно пространство.

V8 има два вградени механизма за събиране на боклука: Scavenge, Mark-Sweep и Mark-Compact.

Scavenge е много бърза техника за събиране на боклука и работи с обекти в Ново пространство . Scavenge е изпълнението на Алгоритъм на Чейни . Идеята е много проста, Ново пространство се разделя на две равни полупространства: To-Space и From-Space. Scavenge GC възниква, когато To-Space е пълен. Той просто разменя пространства To и From и копира всички живи обекти в To-Space или ги популяризира в едно от старите пространства, ако са оцелели след две очисти и след това се изтрива изцяло от пространството. Scavenges са много бързи, но те имат за сметка на запазване на двойна по размер купчина и постоянно копиране на обекти в паметта. Причината да се използват чисти е, защото повечето обекти умират млади.

Mark-Sweep & Mark-Compact е друг тип събирач на боклук, използван във V8. Другото име е пълен боклук. Той маркира всички действащи възли, след това мете всички мъртви възли и дефрагментира паметта.

Съвети за производителност и отстраняване на грешки на GC

Въпреки че за уеб приложенията високата производителност може да не е толкова голям проблем, все пак ще искате да избегнете течове на всяка цена. По време на фазата на маркиране в пълен GC приложението всъщност е на пауза, докато събирането на боклука не завърши. Това означава, че колкото повече обекти имате в купчината, толкова по-дълго ще отнеме изпълнението на GC и по-дълго потребителите ще трябва да чакат.

Винаги давайте имена на затварянията и функциите

Много по-лесно е да проверите следите и купчините на стека, когато всички ваши затваряния и функции имат имена.

db.query('GIVE THEM ALL', function GiveThemAllAName(error, data) { ... })

Избягвайте големи обекти в горещи функции

В идеалния случай искате да избягвате големи обекти вътре в горещите функции, така че всички данни да се вписват Ново пространство . Всички операции на процесора и паметта трябва да се изпълняват във фонов режим. Също така избягвайте задействанията за деоптимизация за горещи функции, оптимизираната гореща функция използва по-малко памет от неоптимизираните.

Горещите функции трябва да бъдат оптимизирани

Горещите функции, които работят по-бързо, но също така консумират по-малко памет, карат GC да работи по-рядко. V8 предоставя някои полезни инструменти за отстраняване на грешки за откриване на неоптимизирани функции или деоптимизирани функции.

кога да използваме node js

Избягвайте полиморфизма за IC в горещи функции

Вградените кешове (IC) се използват за ускоряване на изпълнението на някои парчета код, или чрез кеширане на достъп до свойството на обекта obj.key или някаква проста функция.

function x(a, b) { return a + b; } x(1, 2); // monomorphic x(1, “string”); // polymorphic, level 2 x(3.14, 1); // polymorphic, level 3

Кога x (a, b) се изпълнява за първи път, V8 създава мономорфна IC. Когато се обадите x втори път V8 изтрива старата интегрална схема и създава нова полиморфна интегрална схема, която поддържа и двата вида операнди цяло число и низ. Когато извикате IC за трети път, V8 повтаря същата процедура и създава друга полиморфна IC от ниво 3.

Има обаче ограничение. След като нивото на IC достигне 5 (може да се променя с –Max_inlining_levels флаг) функцията става мегаморфна и вече не се счита за оптимизируема.

Интуитивно разбираемо е, че мономорфните функции се изпълняват най-бързо и освен това имат по-малък отпечатък от паметта.

Не добавяйте големи файлове в паметта

Това е очевидно и добре познато. Ако имате големи файлове за обработка, например голям CSV файл, прочетете го ред по ред и обработвайте на малки парчета, вместо да зареждате целия файл в паметта. Има доста редки случаи, когато един ред csv би бил по-голям от 1mb, като по този начин ви позволява да го поберете Ново пространство .

Не блокирайте основната нишка на сървъра

Ако имате някакъв горещ API, който отнема известно време за обработка, като например API за преоразмеряване на изображения, преместете го в отделна нишка или го превърнете във фонова работа. Интензивните операции на процесора биха блокирали основната нишка, принуждавайки всички останали клиенти да чакат и продължават да изпращат заявки. Необработените данни за заявки ще се натрупват в паметта, като по този начин принуждават пълния GC да отнеме повече време, за да завърши.

Не създавайте ненужни данни

Веднъж имах странен опит с restify. Ако изпратите няколкостотин хиляди заявки към невалиден URL адрес, тогава паметта на приложението бързо ще нарасне до до сто мегабайта, докато след няколко секунди не започне пълен GC, когато всичко ще се върне към нормалното. Оказва се, че за всеки невалиден URL адрес restify генерира нов обект за грешка, който включва дълги следи от стека. Това принуди новосъздадените обекти да бъдат разпределени в Голямо обектно пространство а не в Ново пространство .

Наличието на достъп до такива данни може да бъде много полезно по време на разработката, но очевидно не е необходимо при производството. Следователно правилото е просто - не генерирайте данни, освен ако със сигурност не ви трябват.

Познайте инструментите си

Последно, но със сигурност не на последно място, е да познавате инструментите си. Има различни дебъгъри, генератори на течове и графики за използване. Всички тези инструменти могат да ви помогнат да направите своя софтуер по-бърз и по-ефективен.

Заключение

Разбирането на начина, по който работи събирането на боклук и оптимизаторът на код на V8, е ключът към ефективността на приложението. V8 компилира JavaScript в родния монтаж и в някои случаи добре написаният код може да постигне производителност, сравнима с GCC компилираните приложения.

И в случай, че се чудите, новото приложение на API за моя клиент на ApeeScape, въпреки че има място за подобрения, работи много добре!

Joyent наскоро пусна нова версия на Node.js, която използва една от най-новите версии на V8. Някои приложения, написани за Node.js v0.12.x, може да не са съвместими с новата версия v4.x. Приложенията обаче ще изпитат огромно подобрение на производителността и използването на паметта в рамките на новата версия на Node.js.

По-добро UX чрез микро-взаимодействия

Ux Дизайн

По-добро UX чрез микро-взаимодействия
Въведение в обекти и препратки в PHP памет

Въведение в обекти и препратки в PHP памет

Back-End

Популярни Публикации
UI срещу UX: Жизненоважното ръководство за дизайн на потребителския интерфейс
UI срещу UX: Жизненоважното ръководство за дизайн на потребителския интерфейс
Приемане на Firebase без сървъри - Мобилните и уеб приложенията стават лесно
Приемане на Firebase без сървъри - Мобилните и уеб приложенията стават лесно
Ценова еластичност 2.0: от теорията към реалния свят
Ценова еластичност 2.0: от теорията към реалния свят
Проучване на мултимодален дизайн - Урок за Adobe XD
Проучване на мултимодален дизайн - Урок за Adobe XD
Максималистичен дизайн и проблемът с минимализма
Максималистичен дизайн и проблемът с минимализма
 
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Научете Markdown: Инструмент за писане за разработчици на софтуер
Научете Markdown: Инструмент за писане за разработчици на софтуер
CloudI: Привеждане на толерантността на Erlang към развитие на полиглот
CloudI: Привеждане на толерантността на Erlang към развитие на полиглот
Внедряване на безсървърни функции Node.js с помощта на Google Cloud
Внедряване на безсървърни функции Node.js с помощта на Google Cloud
Презентационен дизайн и изкуството на визуалното разказване на истории
Презентационен дизайн и изкуството на визуалното разказване на истории
Популярни Публикации
  • в какво се пише node js
  • как да намеря софтуерни инженери
  • калкулатор на общата цена на служителите
  • какво е прототипирането в дизайна
  • как да направите жетон
  • node js какво е това
Категории
  • Растеж На Приходите
  • Финансови Процеси
  • Дизайнерски Живот
  • Съвети И Инструменти
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt