Как Node.js обеспечивает параллелизм?

Как Node.js обеспечивает параллелизм?

14 января 2023 г.

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

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

Одновременное выполнение нескольких рейсов делает возможным авиаперелет. Эта способность также известна как параллелизм и является ключом к выполнению большего объема работы с ограниченным временем и ресурсами.

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

Как бы вы отреагировали, если бы вы посетили главную страницу Amazon и получили сообщение о том, что максимальное количество пользователей уже обращается к веб-сайту и вам нужно дождаться своей очереди?

Скорее всего, вы никогда больше не посетите этот веб-сайт, и Amazon потеряет потенциального клиента.

Как разработчики, когда мы используем платформу (например, веб-сервер) для запуска приложения, мы ожидаем, что платформа будет поддерживать параллельное поведение. Часто модель параллелизма платформ или фреймворков довольно проста для понимания.

Но я заметил, что разработчики часто путаются в параллелизме в Node.js. Они знают о цикле событий, но не знают, как он на самом деле работает.

В этом посте моя цель — прояснить всю путаницу, связанную с параллелизмом в Node.js.

Самое большое препятствие для параллелизма

Почему сложно добиться параллелизма?

Есть ли фундаментальное препятствие для параллелизма в программных системах?

Да.

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

Операции ввода-вывода являются одной из основных операций компьютерной системы. Но они также и самые медленные.

Доступ к оперативной памяти занимает порядка наносекунд. Доступ к диску происходит еще медленнее (порядка миллисекунд).

Та же история и с пропускной способностью.

Скорость передачи в ОЗУ составляет порядка ГБ/сек, а на диске и в сети может варьироваться от МБ/сек до ГБ/сек.

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

Кроме того, ввод-вывод — это не только проблема программного обеспечения. Человеческий фактор, наверное, даже важнее. Самая большая задержка в приложении связана с ожиданием действий пользователя (например, щелчков мышью и нажатий клавиш). Эти задержки на много порядков больше, чем задержки, вызванные доступом к диску или сетевой задержкой.

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

Как платформы могут преодолеть блокировку ввода-вывода?

Без сомнения, медленный и блокирующий ввод-вывод является самым большим препятствием для параллелизма.

Итак, как некоторые платформы решают эту проблему?

Давайте рассмотрим несколько популярных методов включения параллелизма.

Многопоточность

Многопоточность — это распространенный подход к поддержке параллелизма. Он используется популярными языками, такими как Java.

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

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

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

Когда поток «блокируется» для операции ввода-вывода, другие запросы не будут затронуты, поскольку эти запросы имеют свои собственные отдельные потоки.

Отсюда и название многопоточность.

См. иллюстрацию ниже

Handling requests using multi-threading

К сожалению, поток недешев с точки зрения системных ресурсов.

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

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

Занят-ожидание

Большинство современных операционных систем поддерживают механизм, известный как неблокирующий ввод-вывод.

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

Итак, как на самом деле обрабатываются данные?

Ответ: занят-жду.

Ожидание занятости — это не что иное, как активный опрос ресурса в цикле до тех пор, пока он не вернет некоторые фактические данные.

Polling is a drag on resources

С ожиданием занятости вы можете использовать один и тот же поток для обработки нескольких запросов ввода-вывода.

Но и алгоритмы опроса неэффективны. Большая часть драгоценного процессорного времени тратится на перебор ресурсов, которые в основном недоступны.

Почему Node.js не может использовать многопоточность

По сравнению с ожиданием или опросом, многопоточность является лучшим подходом для достижения параллелизма. Многие языки программирования и платформы успешно используют многопоточность.

Однако Node.js не может использовать многопоточность. Это связано с тем, что Node.js использует JavaScript, а JavaScript является однопоточным.

Значит, надежды нет?

На самом деле есть еще один механизм включения неблокирующих ресурсов. Этот механизм известен как Демультиплексирование событий. Вы также можете назвать ее Службой уведомлений о событиях.

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

How does an Event Demultiplexer Work

Процесс демультиплексирования событий состоит из трех важных шагов:

  • Сначала ресурсы добавляются в список наблюдения. С каждым ресурсом связана операция, например чтение или запись.

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

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

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

Нет необходимости в многопоточности. Нет необходимости в напряженном ожидании и опросе. Достаточно одного потока с очень небольшим временем простоя. Задачи распределены по времени, а не разделены между несколькими потоками.

The magic of single-threaded webserver

Цикл событий Node.js

Рассмотрев все объяснения, мы теперь можем построить модель того, как работает цикл обработки событий Node.js.

Посмотрите на диаграмму ниже, на которой показаны все шаги в цикле обработки событий.

The Event Loop in all its glory

Давайте пройдемся по всем шагам один за другим:

* ШАГ A. Приложение создает новый запрос ввода-вывода и отправляет его в демультиплексор событий. Наряду с запросом приложение также предоставляет обработчик или обратный вызов. Это похоже на указание демультиплексору, с кем связаться после завершения запроса ввода-вывода. Вызов демультиплексора не блокируется, и управление немедленно возвращается обратно в приложение.

* ШАГ B. Демультиплексор событий ожидает событий от ресурсов ввода-вывода в своем списке наблюдения. Когда группа операций ввода-вывода завершена, Демультиплексор событий помещает соответствующие события в Очередь событий. * * ШАГ C. Цикл событий перебирает элементы в очереди событий. Технически Очередь событий — это не одна очередь, а несколько очередей, которые обрабатываются на разных этапах. Если интересно, вы можете узнать больше о различных очередях цикла событий.

* ШАГ D. Для каждого события Цикл событий запускает обработчик. Информация об обработчике была передана в Цикл событий демультиплексором на ШАГЕ B.

* ШАГ E. Обработчики являются частью кода приложения. Мы также знаем их как обратные вызовы. После выполнения обратного вызова он может вернуть управление Циклу событий. Это обозначается путем E1. Однако обработчик может также запросить новые асинхронные операции, например путь E2. Путь E2 приводит к отправке новых операций в демультиплексор событий.

* ШАГ 6. Когда Очередь событий пуста, цикл снова блокируется на Демультиплексоре событий и ожидает следующего цикла.

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

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

Заключение

Node.js является параллельным из-за цикла событий. А Цикл событий стал возможен благодаря демультиплексору.

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

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

Если вы нашли этот пост полезным, рассмотрите возможность поделиться им с друзьями и коллегами.

Вы также можете связаться со мной на других платформах:

Твиттер

LinkedIn


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE