Цикл событий Nodejs

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

  • libev / libuv
  • Цикл событий javascript v8

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

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

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


person Tamil    schedule 21.05.2012    source источник


Ответы (8)


Я лично читал исходный код node.js & v8.

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

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

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

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

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

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

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

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

Например:

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

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

person ShrekOverflow    schedule 18.06.2012
comment
Полагаться на то, что libuv внутренне использует libev, - хороший способ сделать ваш код не кросс-платформенным. Вам следует заботиться только об общедоступном интерфейсе libuv. - person Raynos; 18.06.2012
comment
@Raynos libuv стремится к тому, чтобы в его x-платформе было несколько библиотек. Верно ? следовательно, использование libuv - хорошая идея - person ShrekOverflow; 18.06.2012
comment
@Abhishek Из документа process.nextTick - в следующем цикле цикла событий вызовите этот обратный вызов. Это не простой псевдоним для setTimeout (fn, 0), он намного эффективнее. К какому циклу событий относится это? Цикл событий V8? - person Tamil; 19.06.2012
comment
Да, с помощью process.nextTick вы просто помещаете функцию в верхнюю часть стека вызовов. - person ShrekOverflow; 19.06.2012
comment
Связанная ссылка: github.com/joyent/node / wiki / - person Florian Margaine; 20.06.2012
comment
Действительно, видеогалерея Yahoo для видео программистов в целом является богатым источником информации, и это видео было потрясающим :-) - person ShrekOverflow; 22.06.2012
comment
Обратите внимание, что libuv больше не реализуется поверх libev. - person strcat; 05.05.2013
comment
тогда это было :-) и да, я думаю, мне стоит обновить пост - person ShrekOverflow; 07.05.2013
comment
Есть ли способ «увидеть» эту очередь событий? Я хотел бы иметь возможность видеть порядок вызовов в стеке и видеть, как туда помещаются новые функции, чтобы лучше понять, что происходит ... есть ли какая-то переменная, которая сообщает вам, что было помещено в очередь событий? - person tbarbe; 08.09.2013
comment
@tbarbe Было бы здорово увидеть это. Где-то здесь программист должен иметь хорошее представление о том, как работает программа. ! - person loveNoHate; 10.03.2014
comment
Я хочу проверить свое понимание. Я просмотрел код V8 и не нашел ни одной ссылки ни на libuv, ни на libev, ни на libEio. Итак, я сделал вывод, что V8 просто предоставляет способ выполнения кода JavaScript, это интерпретатор JS. Цикл событий обеспечивается самим Node с помощью libuv. В зависимости от платформы, которую мы рассматриваем, libuv предоставит нам цикл событий через libev и обратную связь по асинхронным событиям с помощью libEio (это верно для Linux / Unix). - person DiegoS; 12.03.2015

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

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

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

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

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

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

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

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

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

person Gireesh Punathil    schedule 26.04.2016
comment
Как вы узнали об этом, можете указать мне источник? - person Suraj Jain; 31.03.2020

Введение в libuv

Проект node.js начался в 2009 году как среда JavaScript, отделенная от браузера. С помощью V8 от Google и libev, node.js объединил модель ввода-вывода - с учетом событий - с языком, который хорошо подходил к стилю программирования; из-за того, как он был сформирован браузерами. По мере роста популярности node.js было важно заставить его работать в Windows, но libev работала только в Unix. Эквивалент Windows механизмов уведомления о событиях ядра, таких как kqueue или (e) poll, - это IOCP. libuv была абстракцией от libev или IOCP в зависимости от платформы, предоставляя пользователям API на основе libev. В версии libuv для node-v0.9.0 libev был удален.

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


Обновление от 09.05.2017

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

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

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

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

Обзор этапов

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

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

person zangw    schedule 02.01.2016
comment
Вы процитировали это In the node-v0.9.0 version of libuv libev was removed, но в nodejs changelog об этом нет описания. github.com/nodejs/node/blob/master/CHANGELOG.md. А если удалить libev, то как теперь выполняется асинхронный ввод-вывод в nodejs? - person intekhab; 07.01.2016
comment
@intekhab, по этой ссылке, я думаю, что libuv на основе libeio можно использовать как цикл событий в node.js. - person zangw; 08.01.2016
comment
@intekhab, я думаю, libuv реализует все функции, связанные с вводом-выводом и опросом. здесь проверьте этот документ: docs.libuv.org/en/v1.x/ loop.html - person mohit kaushik; 15.01.2020

В архитектуре NodeJs есть один цикл событий.

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

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

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

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

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

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

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

person Peter Hauge    schedule 25.08.2015

В libuv есть только один цикл обработки событий, V8 - это просто механизм выполнения JS.

person Warren Zhou    schedule 06.01.2016

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

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

Функция pbkdf2 имеет реализацию 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>);

ресурс: https://github.com/nodejs/node/blob/master/src/node_crypto.cc

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

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

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

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

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

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

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

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

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

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

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

person Daniel    schedule 17.02.2019

Проще говоря, цикл событий узла - это цикл или цикл на архитектурном уровне, который помогает коду Javascript обрабатывать асинхронный код.

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

person Nouman Dilshad    schedule 23.06.2021