Node.js
- позволяет нам выполнять код вне браузера. Это серверная платформа для работы с javaScript
через движок v8, что позволяет нам писать бэкенд. Мы можем написать и фронт и бек на одном javaScript
.
Node.js
может выполнять роль веб сервера.Node.js
умеет работать с внешними библиотеками, благодаря npm.Node.js
позволяет взаимодействовать с операционной системой и устройствами ввода/вывода.
устройства ввода/вывода - это устройства с которых компьютер получает или передает информацию. Например компьютерная мышь и клавиатура это устройства ввода. А монитор и колонки - устройства вывода. Так же есть устройства ввода и вывода - это флешка например или жесткий диск.
javaScript
можно уже начинать изучение Node.js
.Node.js
распределяет ресурсы грамотнее, что позволяет ему быть очень быстрым.В первую очередь это движок v8 с помощью которого javaScript
код преобразуется в машинный код.
А также libuv
- это библиотека написанная на языке C
, которая отвечает за асинхронный неблокирующий ввод/вывод операций в Node.js
.
Libuv
позволяет сделать node.js
кроссплатформенным.
Именно libuv
знает как работать на разных операционных системах.
Мы используем например метод для работы с файловой системой, node.js
внутри себя как раз будет использовать libuv
и мы даже не будем задумываться на какой операционной системе находимся.
Теперь немного поговорим про то, какие есть модели ввода/вывода. Как можно работать с компьютером на уровне операционной системы.
Мы выполняем команду за командой и если есть трудоёмкая операция, то поток будет заблокирован пока её не выполнит. То есть системный вызов(обращение к ядру операционной системы), ждет когда операция будет закончена.
Для решения такой проблемы, операции выполняют в разных потоках.
Один поток работает с базой данных, другой с файловой системой, третий работает с сетью и так далее.
Так работают классические веб сервера на java
.
У блокирующего ввода и вывода есть минусы:
Потребление большого количества ресурсов и сложность в управлении потоками.
Как видим потоки некоторое время находятся в состоянии простоя ожидая новых данных получаемых из связанных с ним соединений. При этом, в это время, они потребляют ресуры.
Чем больше у нас потоков, тем больше мы будем тратить времени на переключение контекста.
Переключение контекста (context switch) - это процесс записи и восстановления состояния процесса или потока таким образом, чтобы в дальнейшем продолжить его выполнение с прерванного места.
Потоки занимают место в оперативной памяти.
В таком режиме сервер работает с одним главным потоком. В неблокирующим режиме системные вызовы возвращают управление немедленно не дожидаясь чтения или записи данных. Основной шаблон реализации такого режима является активный опрос ресурса в цикле до тех пор, пока не будут возвращены какие либо фактические данные - это называется цикл ожидания. Поток при всем этом не блокируется.
busy waiting
- Занят ожиданием или проще можно назвать как цикл ожидания — метод,
при котором процесс многократно проверяет, верно ли условие, например, доступен ли ввод с клавиатуры.
С помощью такой техники уже можно обрабатывать разные ресурсы в одном потоке, но это все еще неэффективно.
Хорошо, что большинство современных операционных систем предоставляют встроенный механизм для более эффективной работы неблокирующего I/O
.
Этот механизм называется синхронным демультиплексором событий.
Он собирает и ставит в очередь события ввода-вывода, поступающие из набора отслеживаемых ресурсов, и блокирует их до тех пор, пока новые события не станут доступны для обработки.
И тут мы переходим к более подробному разбору libuv
.
Начнем с того, что node.js
однопоточный. Но в основе node.js
лежит libuv
, который занимается операциями ввода и вывода и может управлять потоками, по умолчанию их 4.
Нужно это для задач ввода/вывода, для работы с файловой системой, для шифрования и так далее. Так как javaScript
по своей природе однопоточный и не готов к таким тяжелым операциям и работе с операционной системой.
Все же он создавался для работы небольших скриптов на сайтах в браузере. Тут на помощь и приходит libuv
.
// Пример не мой!!!
const crypto = require('crypto');
const start = Date.now(); // время начала
// используем функцию для шифровки пароля 1000000 - это кол итераций
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('1 закончил за', Date.now() - start ); // вычитаем из времени выполнения время начала скрипта.
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('2 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('3 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('4 закончил за', Date.now() - start );
});
// у нас получился такой вывод в миллисекундах
/*
1 закончил за 1128
2 закончил за 1196
3 закончил за 1247
4 закончил за 1269
*/
Как видим выполнение плюс минут за одно время, но если мы добавим еще одну операцию.
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('1 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('2 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('3 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('4 закончил за', Date.now() - start );
});
crypto.pbkdf2('qwerty', '5', 1000000, 64, 'sha512', () => {
console.log('5 закончил за', Date.now() - start );
});
/*
2 закончил за 1086
1 закончил за 1095
3 закончил за 1115
4 закончил за 1139
5 закончил за 1888
*/
Пятый запуск отработал гораздо позже. Если добавим еще, то будет так же. Первые четыре выполнения всегда будут плюс минус равны, остальные уже будут отставать. Наши первые 4 вызова занимают 4 потока, 5 функция ждет и тот поток который освободится раньше всех, забирает на себя 5 функцию, этим и объясняется разница во времени.
Libuv
состоит из:
callback
.callback
)Все эти компоненты вместе образуют шаблон под названием reactor pattern
.
Вот как работает этот шаблон:
Каждая операционная система имеет свой собственный интерфейс для демультиплексора событий: epoll в Linux, kqueue в Mac OS X и API I/O Completion Port (IOCP) в Windows. Поэтому основная команда Node.js создала библиотеку libuv с целью сделать Node.js совместимым со всеми основными платформами.
30.12.2022