Какую роль играет движок V8 в Node.js?

В последние дни я пытался понять, как стиль на основе событий Node.js может обрабатывать гораздо больше одновременных запросов, чем классический подход многопоточности. В конце концов, речь идет о меньшем объеме памяти и переключениях контекста, потому что Node.js использует только пару потоков (один поток V8 и несколько рабочих потоков C ++ плюс основной поток libuv).

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

Я так понимаю, что работает Node.js.

Мне было интересно, что дает Node.js возможность обрабатывать HTTP-запросы. На основании того, что я читал до сих пор, я понимаю, что libuv - это то, кто это работает:

Дескрипторы представляют собой долгоживущие объекты, способные выполнять определенные операции в активном состоянии. Некоторые примеры: дескриптор подготовки получает свой обратный вызов один раз на каждой итерации цикла, когда он активен, а дескриптор TCP-сервера получает свой обратный вызов соединения каждый раз, когда возникает новое соединение.

введите здесь описание изображения

Итак, поток, ожидающий входящего HTTP-запроса, является основным потоком libuv, который выполняет цикл событий libuv.

Итак, когда мы пишем

const http = require('http');

const hostname = '127.0.0.1';
const port = 1337;

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World\n');
}).listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

... Я вставляю в libuv обратный вызов, который будет выполняться в движке V8 при поступлении запроса?

Тогда порядок событий будет

  1. Приходит TCP-пакет
  2. ОС создает событие и отправляет в цикл событий
  3. Цикл событий обрабатывает событие и создает событие V8.

Если я выполню код блокировки внутри анонимной функции, которая обрабатывает запрос, я заблокирую поток V8.

Чтобы этого избежать, мне нужно выполнить неблокирующий код, который будет выполняться в другом потоке. Я предполагаю, что этот "другой поток" является основным потоком libuv, где

сетевой ввод-вывод всегда выполняется в одном потоке, каждый поток цикла

Этот поток не будет блокироваться, поскольку использует асинхронные системные вызовы ОС.

epoll в Linux, kqueue в OSX и других BSD, порты событий в SunOS и IOCP в Windows

Я также предполагаю, что https://nodejs.org/dist/latest-v5.x/docs/api/http.html#http_http_request_options_callback является используя libuv для этого.

Точно так же, если мне нужно выполнить некоторый файловый ввод-вывод, не блокируя поток V8,
я буду использовать FileSystem модуля Node. На этот раз основной поток libuv не может справиться с этим неблокирующим образом, потому что ОС не предлагает эту функцию.

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

В этом случае нужен классический пул потоков, чтобы не блокировать цикл обработки событий libuv.

Теперь, если мне нужно запросить базу данных, вся ответственность за то, чтобы не блокировать ни поток V8, ни поток libuv, находится в руках разработчика драйвера. Если драйвер не использует libuv, он заблокирует двигатель V8.

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

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

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


person gabrielgiussi    schedule 23.01.2016    source источник


Ответы (3)


Насколько я знаю, разница в основном заключается в асинхронном вводе-выводе. В традиционных серверах «процесс за запрос» или «поток за запрос» ввод-вывод, особенно сетевой ввод-вывод, традиционно является синхронным вводом-выводом. Node.js использует меньше потоков, чем Apache или что-то еще, и он может обрабатывать трафик в основном потому, что он использует асинхронный сетевой ввод-вывод.

Node.js нужен V8 для реальной интерпретации JS-кода и превращения его в машинный код. Libuv нужен для реального ввода-вывода. Больше я ничего не знаю :)

person Alexander Mills    schedule 09.01.2017

Есть отличный пост о node.js движке V8: Как работает JavaScript: внутри механизма V8 + 5 советов по написанию оптимизированного кода. Он объяснил многие подробные аспекты движка и несколько замечательных рекомендаций при его использовании.

Проще говоря, V8 engine (и другие javascript engines) выполняет javascript код. Однако V8 engine обеспечивает более высокую производительность по сравнению с другими.

V8 переводит код JavaScript в более эффективный машинный код вместо использования интерпретатора. Он компилирует код JavaScript в машинный код при выполнении, реализуя компилятор JIT (Just-In-Time) ...

person haotang    schedule 24.08.2017

Ввод-вывод является неблокируемым и асинхронным через libuv, в основе которого используются примитивы ОС, такие как epoll или аналогичные, в зависимости от платформы, чтобы сделать ввод-вывод неблокирующим. Цикл событий Nodejs возвращает событие в очередь, когда происходит событие на fd (например, сокет tcp)

person shashank    schedule 26.09.2017