Вопрос по node.js, javascript – Nodejs Event Loop

128

Есть ли внутри два цикла событий в архитектуре nodejs?

libev/libuv v8 javascript event loop

При запросе ввода-вывода узел ставит в очередь запрос к libeio, который, в свою очередь, уведомляет о доступности данных через события, используя libev, и, наконец, эти события обрабатываются циклом событий v8 с использованием обратных вызовов?

В основном, как libev и libeio интегрированы в архитектуру nodejs?

Есть ли доступная документация, чтобы дать четкое представление о внутренней архитектуре nodejs?

Ваш Ответ

7   ответов
163

Я столкнулся с подобной проблемой, как ты, когда попытался понять архитектуру node.js для написания собственных модулей.

То, что я публикую здесь, - это мое понимание node.js, и это может быть немного не так.

Libev - это цикл событий, который фактически выполняется внутри узла node.js для выполнения простых операций цикла событий. Первоначально написано для * nix систем. Libev предоставляет простой, но оптимизированный цикл событий для запуска процесса. Вы можете прочитать больше о libevВо.

LibEio - это библиотека для асинхронного ввода-вывода. Он обрабатывает файловые дескрипторы, обработчики данных, сокеты и т. Д. Подробнее об этом можно прочитать здесьВо.

LibUv - это уровень абстракции в верхней части libeio, libev, c-ares (для DNS) и iocp (для windows asynchronous-io). LibUv выполняет, поддерживает и управляет всеми операциями ввода-вывода и событиями в пуле событий. (в случае освобождения резерва). Вы должны проверить Учебник Райана Даля на libUv. Это начнет понимать для вас, как работает libUv, и тогда вы поймете, как работает node.js поверх libuv и v8.

Чтобы понять только цикл событий javascript, вы должны рассмотреть просмотр этих vi, deos

JS-конференции JSConf2011 (имеет очень раздражающий sfx) Понимание событийно-ориентированного программирования Понимание цикла событий node.js

Чтобы увидеть, как libeio используется с node.js для создания асинхронных модулей, вы должны увидеть этот пример.

В основном внутри node.js происходит то, что цикл v8 выполняется и обрабатывает все части javascript, а также модули C ++ [когда они работают в главном потоке (согласно официальной документации, node.js является однопоточным)]. Когда вне основного потока, libev и libeio обрабатывают его в пуле потоков, а libev обеспечивает взаимодействие с основным циклом. Насколько я понимаю, в node.js есть 1 постоянный цикл обработки событий: это цикл обработки событий v8. Для обработки асинхронных задач C ++ он использует пул потоков [via libeio & libev].

Например

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

Который появляется во всех модулях, обычно вызывает функциюTask в пуле потоков. Когда он завершен, он вызываетAfterTaskункция @ в главном потоке. В то время какEio_REQUEST - это обработчик запроса, который может быть структурой / объектом, мотивом которого является обеспечение связи между пулом потоков и основным потоком.

Конечно, libuv - хорошая идея Raynos
@ Абхишек От Докаprocess.nextTick - В следующем цикле вокруг цикла обработки событий вызовите этот обратный вызов. Это не простой псевдоним для setTimeout (fn, 0), он гораздо эффективнее. К какому циклу событий это относится? Цикл событий V8? Tamil
Мне понравился этот урок Райан Yuiblog.com / блог / 2010/05/20 / видео-Dahl Tamil
Есть ли способ «увидеть» эту очередь событий? Мне бы хотелось видеть порядок вызовов в стеке и видеть, как туда помещаются новые функции, чтобы лучше понять, что происходит ... есть ли переменная, которая сообщает вам, что было передано в очередь событий? tbarbe
@ Raynos libuv стремится удостовериться, что в его x-platfousing есть несколько библиотек. Правильно ? следовательно, использование libuv - хорошая иде ShrekOverflow
12

Node.js Модель цикла событий

риложения @Node работают в однопоточной модели, управляемой событиями. Однако Node реализует пул потоков в фоновом режиме, так что работа может быть выполнена.

Node.js добавляет работу в очередь событий, а затем один поток запускает цикл обработки событий. Цикл обработки событий захватывает верхний элемент в очереди событий, выполняет его, а затем захватывает следующий элемент.

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

Цикл событий начинает сталкиваться с проблемами, когда наши функции приложения блокируются при вводе / выводе.

Node.js использует обратные вызовы событий, чтобы избежать необходимости блокировать ввод-вывод. Поэтому любые запросы, которые выполняют блокировку ввода-вывода, выполняются в другом потоке в фоновом режиме.

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

18

что некоторые из обсуждаемых сущностей (например, libev и т. Д.) Утратили актуальность из-за того, что это было давно, но я думаю, что вопрос все еще имеет большой потенциал.

Позвольте мне попытаться объяснить работу управляемой событиями модели с помощью абстрактного примера в абстрактной среде UNIX в контексте Node на сегодняшний день.

Перспектива программы:

еханизм @Script запускает выполнение скрипта. Всякий раз, когда встречается связанная с процессором операция, она выполняется inline (реальной машиной) во всей своей полноте. Каждый раз, когда встречается операция, связанная с вводом / выводом, запрос и обработчик его завершения регистрируются «механизмом событий» (виртуальной машиной) Повторите операции таким же образом, как описано выше, до завершения сценария. Операция с привязкой к ЦП - выполнить линейные, связанные с вводом / выводом, запрос к оборудованию, как указано выше. Когда ввод-вывод завершается, слушатели перезваниваются.

Вышеуказанный механизм называется libuv AKA framework цикла событий. Node использует эту библиотеку для реализации своей модели программирования, управляемой событиями.

Перспектива узла:

Есть один поток для размещения среды выполнения. Подберите сценарий пользователя. Скомпилируйте его в нативный [leverage v8] Загрузите двоичный файл и прыгните в точку входа. Скомпилированный код выполняет встроенные действия, связанные с процессором, используя программные примитивы. Многие коды ввода / вывода и код таймера имеют встроенные переносы. Например, сетевой ввод / вывод. Так что вызовы ввода / вывода направляются от сценария к мостам C ++, а дескриптор ввода / вывода и обработчик завершения передаются в качестве аргументов. Нативный код выполняет цикл libuv. Он получает цикл, ставит в очередь событие низкого уровня, которое представляет ввод / вывод, и встроенную оболочку обратного вызова в структуру цикла libuv. Нативный код возвращается в сценарий - в данный момент ввод / вывод не производится! Элементы выше повторяются много раз, пока весь код не-I / O не будет выполнен, и весь код I / O будет зарегистрирован в libuv. Наконец, когда в системе не осталось ничего для выполнения, узел передает управление libuvlibuv вступает в действие, собирает все зарегистрированные события, запрашивает операционную систему, чтобы получить их работоспособность. Те, которые готовы к вводу / выводу в неблокирующем режиме, извлекаются, выполняются операции ввода / вывода и выполняются их обратные вызовы. Один за другим То, что еще не готово (например, чтение сокета, для которого другая конечная точка еще ничего не написала), будет проверяться ОС до тех пор, пока они не станут доступны. Цикл внутренне поддерживает постоянно увеличивающийся таймер. Когда приложение запрашивает отложенный обратный вызов (например, setTimeout), это значение внутреннего таймера используется для вычисления правильного времени для запуска обратного вызова.

Пока большинство функций обслуживаются таким образом, некоторые (асинхронные версии) файловых операций выполняются с помощью дополнительных потоков, хорошо интегрированных в libuv. В то время как операции сетевого ввода-вывода могут ожидать в ожидании внешнего события, такого как другая конечная точка, отвечающая данными и т. Д., Файловые операции требуют некоторой работы от самого узла. Например, если вы откроете файл и подождите, пока fd будет готов с данными, этого не произойдет, так как на самом деле никто не читает! В то же время, если вы читаете из файла, встроенного в основной поток, это может потенциально блокировать другие действия в программе и может создавать видимые проблемы, поскольку файловые операции очень медленны по сравнению с действиями, связанными с процессором. Таким образом, внутренние рабочие потоки (настраиваемые с помощью переменной среды UV_THREADPOOL_SIZE) используются для работы с файлами, в то время как управляемая событиями абстракция работает без изменений с точки зрения программы.

Надеюсь это поможет

Хорошее и кристально чистое объяснение. Спасибо Tahsin Turkoz
просто хорошо .... devprashant
16

Введение в libuv

The Node.jsроект @ начался в 2009 году, когда среда JavaScript была отделена от браузера. Используя Google's V8 и Марк Леманн Libev, node.js объединил модель ввода-вывода - Evented - с языком, который хорошо подходил для стиля программирования; из-за того, как он был сформирован браузерами. По мере роста популярности node.js важно было заставить его работать в Windows, но libev работал только в Unix. В Windows эквивалентом механизмов уведомления о событиях ядра, таких как kqueue или (e) poll, является IOCP. libuv был абстракцией вокруг libev или IOCP в зависимости от платформы, предоставляя пользователям API на основе libev. В версии libuv @ node-v0.9libev был удален.

Также одна картинка, которая описывает цикл событий в Node.js BusyRich

Обновление 05.09.2017

Расскажи этот документ Node.js цикл событий,

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

┌───────────────────────┐ ┌─>│ timers │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ I/O callbacks │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ idle, prepare │ │ └──────────┬────────────┘ ┌───────────────┐ │ ┌──────────┴────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └──────────┬────────────┘ │ data, etc. │ │ ┌──────────┴────────────┐ └───────────────┘ │ │ check │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ └──┤ close callbacks │ └───────────────────────┘

примечание: каждое поле будет называться «фазой» цикла событий.

Phases Overview

Таймеры: эта фаза выполняет обратные вызовы, запланированныеsetTimeout() а такжеsetInterval(). Обратные вызовы ввода / вывода: выполняет почти все обратные вызовы, кроме Закрыть обратные вызовы, запланированные таймерами, иsetImmediate(). на холостом ходу, подготовка: используется только для внутреннего использования.опро: получить новые события ввода / вывода; узел будет блокировать здесь при необходимости.че: setImmediate() обратные вызовы вызываются здесь. Закрыть обратные вызовы: напримерsocket.on('close', ...).

Между каждым запуском цикла обработки событий Node.js проверяет, ожидает ли он каких-либо асинхронных операций ввода-вывода или таймеров, и корректно завершает работу, если их нет.

Вы цитировали это "In the node-v0.9.0 version of libuv libev was removed ", но нет описания этого в nodejschangelog. Github.com / nodejs / узел / блоб / ведущий / CHANGELOG.md. И если libev удален, то как теперь выполняется асинхронный ввод-вывод в nodejs? intekhab
@ intekhab, За этоссылк, Я думаю, что libuv, основанный на libeio, может быть использован как цикл обработки событий в node.js. zangw
8

V8 - просто движок JS.

0

я тоже сомневался, содержит ли NodeJS 2 цикла событий? После долгих исследований и обсуждений с одним из участников V8 я получил следующие концепции.

Цикл событий - фундаментальная абстрактная концепция модели программирования JavaScript. Таким образом, механизм V8 обеспечивает реализацию по умолчанию для цикла обработки событий, какие устройства для вставки (браузер, узел) могут заменить или расширить. Ребята, вы можете найти реализацию V8 по умолчанию в цикле событийВо В NodeJS существует только один цикл событий, который предоставляется средой выполнения узла. Реализация цикла событий V8 по умолчанию была заменена реализацией цикла событий NodeJ
0

Thepbkdf2 function имеет реализацию JavaScript, но фактически делегирует всю работу, которую нужно выполнить, стороне C ++.

env->SetMethod(target, "pbkdf2", PBKDF2);
  env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
  env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
  env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1);
  NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
  NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
  NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
  NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
  NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
  env->SetMethod(target, "randomBytes", RandomBytes);
  env->SetMethodNoSideEffect(target, "timingSafeEqual", TimingSafeEqual);
  env->SetMethodNoSideEffect(target, "getSSLCiphers", GetSSLCiphers);
  env->SetMethodNoSideEffect(target, "getCiphers", GetCiphers);
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
  env->SetMethodNoSideEffect(target, "getCurves", GetCurves);
  env->SetMethod(target, "publicEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_encrypt_init,
                                         EVP_PKEY_encrypt>);
  env->SetMethod(target, "privateDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_decrypt_init,
                                         EVP_PKEY_decrypt>);
  env->SetMethod(target, "privateEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_sign_init,
                                         EVP_PKEY_sign>);
  env->SetMethod(target, "publicDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_verify_recover_init,
                                         EVP_PKEY_verify_recover>);

resource:https: //github.com/nodejs/node/blob/master/src/node_crypto.c

Модуль Libuv несет еще одну ответственность, которая имеет отношение к некоторым очень специфическим функциям в стандартной библиотеке.

Для некоторых стандартных вызовов библиотечных функций сторона Node C ++ и Libuv решают полностью выполнить дорогостоящие вычисления вне цикла обработки событий.

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

По умолчанию Libuv создает 4 потока в этом пуле потоков.

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

Многие функции, включенные в стандартную библиотеку Node, автоматически используют этот пул потоков.pbkdf2 функция является одним из них.

Наличие этого пула потоков очень важно.

Таким образом, Node не является однопоточным, потому что существуют другие потоки, которые Node использует для выполнения некоторых дорогостоящих задач.

Если пул событий отвечал за выполнение дорогостоящей вычислительной задачи, то наше приложение Node больше ничего не могло сделать.

Наш процессор выполняет все инструкции внутри потока одну за другой.

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

Похожие вопросы