Я лично читал исходный код node.js & v8.
Я столкнулся с подобной проблемой, как и вы, когда пытался понять архитектуру node.js, чтобы писать собственные модули.
То, что я публикую здесь, - это мое понимание node.js, и это тоже может быть немного не по плану.
Libev - это цикл событий, который фактически выполняется внутри node.js для выполнения простых операций цикла событий. Он написан изначально для систем * nix. Libev предоставляет простой, но оптимизированный цикл обработки событий для запуска процесса. Подробнее о libev можно прочитать здесь .
LibEio - это библиотека для асинхронного выполнения ввода-вывода. Он обрабатывает файловые дескрипторы, обработчики данных, сокеты и т. Д. Подробнее об этом можно прочитать здесь .
LibUv - это уровень абстракции поверх libeio, libev, c-ares (для DNS) и iocp (для asynchronous-io Windows). 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
это обработчик запроса, который может быть структурой / объектом, целью которого является обеспечение связи между пулом потоков и основным потоком.
process.nextTick
- в следующем цикле цикла событий вызовите этот обратный вызов. Это не простой псевдоним для setTimeout (fn, 0), он намного эффективнее. К какому циклу событий относится это? Цикл событий V8?Похоже, что некоторые из обсуждаемых сущностей (например, libev и т. Д.) Потеряли актуальность из-за того, что прошло некоторое время, но я думаю, что у этого вопроса все еще есть большой потенциал.
Позвольте мне попытаться объяснить работу модели, управляемой событиями, на абстрактном примере в абстрактной среде UNIX, в контексте Node, на сегодняшний день.
Перспектива программы:
Механизм обработки событий, описанный выше, называется структурой цикла событий libuv AKA. Node использует эту библиотеку для реализации своей модели программирования, управляемой событиями.
Перспектива узла:
Хотя большинство функций обслуживается таким образом, некоторые (асинхронные версии) файловые операции выполняются с помощью дополнительных потоков, хорошо интегрированных в libuv. В то время как операции сетевого ввода-вывода могут ожидать внешнего события, такого как другая конечная точка, отправляющая данные и т. Д., Файловые операции требуют некоторой работы от самого узла. Например, если вы открываете файл и ждете, пока fd будет готов с данными, этого не произойдет, поскольку на самом деле никто не читает! В то же время, если вы читаете из файла, встроенного в основной поток, это может потенциально блокировать другие действия в программе и может сделать видимыми проблемы, поскольку файловые операции очень медленные по сравнению с действиями, связанными с процессором. Таким образом, внутренние рабочие потоки (настраиваемые с помощью переменной среды UV_THREADPOOL_SIZE) используются для работы с файлами,
Надеюсь это поможет.
источник
Введение в libuv
Также одна картинка, описывающая цикл событий в Node.js от @ BusyRich
Обновление 09.05.2017
В этом документе цикл событий Node.js ,
На следующей диаграмме показан упрощенный обзор порядка операций цикла событий.
примечание: каждое окно будет называться «фазой» цикла событий.
Обзор фаз
setTimeout()
иsetInterval()
.setImmediate()
.setImmediate()
здесь вызываются обратные вызовы.socket.on('close', ...)
.Между каждым запуском цикла событий Node.js проверяет, ожидает ли он каких-либо асинхронных операций ввода-вывода или таймеров, и аккуратно завершает работу, если их нет.
источник
In the node-v0.9.0 version of libuv libev was removed
", но в nodejs об этом нет описанияchangelog
. github.com/nodejs/node/blob/master/CHANGELOG.md . И если libev будет удалена, то как теперь выполняется асинхронный ввод-вывод в nodejs?В архитектуре NodeJs есть один цикл событий.
Модель цикла событий Node.js
Узловые приложения работают в однопоточной модели, управляемой событиями. Однако Node реализует пул потоков в фоновом режиме, чтобы можно было выполнять работу.
Node.js добавляет работу в очередь событий, а затем запускает единственный поток, выполняющий цикл событий. Цикл событий захватывает верхний элемент в очереди событий, выполняет его, а затем захватывает следующий элемент.
При выполнении кода, который дольше живет или имеет блокирующий ввод-вывод, вместо прямого вызова функции он добавляет функцию в очередь событий вместе с обратным вызовом, который будет выполнен после завершения функции. Когда все события в очереди событий Node.js были выполнены, приложение Node.js завершает работу.
Цикл событий начинает вызывать проблемы, когда функции нашего приложения блокируются при вводе-выводе.
Node.js использует обратные вызовы событий, чтобы не ждать блокировки ввода-вывода. Следовательно, любые запросы, которые выполняют блокирующий ввод-вывод, выполняются в другом потоке в фоновом режиме.
Когда событие, которое блокирует ввод-вывод, извлекается из очереди событий, Node.js извлекает поток из пула потоков и выполняет функцию там, а не в основном потоке цикла событий. Это предотвращает задержку блокирующим вводом-выводом остальных событий в очереди событий.
источник
В libuv есть только один цикл событий, V8 - это просто механизм выполнения JS.
источник
Как новичок в javascript, я тоже сомневался, содержит ли NodeJS 2 цикла событий? После долгого исследования и обсуждения с одним из участников V8 я получил следующие концепции.
источник
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 не могло бы делать ничего другого.
Наш ЦП выполняет все инструкции внутри потока одну за другой.
Используя пул потоков, мы можем делать другие вещи внутри цикла событий во время выполнения вычислений.
источник