Интервьюер спросил: как выполнять асинхронные вызовы ввода-вывода между узлом и нижележащим уровнем.

Node.js JavaScript
Интервьюер спросил: как выполнять асинхронные вызовы ввода-вывода между узлом и нижележащим уровнем.

В этой статье вы можете узнать:

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

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

Поймите концепции, которые нужно изучить в первую очередь в этой статье.

Классификация модулей Node.js

Модули Nodejs можно разделить на следующие три категории:

  • Основной модуль (собственный модуль): он включен в исходный код Node.js и скомпилирован в исполняемый двоичный файл модуля JavaScript Node.js, который на самом деле является файлом js в каталогах lib и deps, таких как обычно используемый http, фс и др.
  • Встроенный модуль (встроенный модуль): Как правило, мы не вызываем его напрямую, а вызываем в нативном модуле, а затем требуем.
  • Сторонние модули: Модули, которые поставляются с исходным кодом, отличным от Node.js, могут в совокупности называться сторонними модулями, такими как экспресс, веб-пакет и т. д.
    • Модули JavaScript, это самые распространенные, обычно мы пишем модули JavaScript при разработке
    • Модуль JSON, это очень просто, просто файл JSON
    • Модуль расширения C/C++, написанный на C/C++, с суффиксом .node после компиляции.

Например, fs.js в каталоге lib исходного кода Node — это собственный модуль, а node_fs.cc в каталоге src, вызываемый fs.js, — это встроенный модуль.

libuv

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

Классическая карта libuv (источник в Интернете)

IOCP

Концепция: Порт завершения ввода-вывода (IOCP) представляет собой интерфейс прикладного программирования, который поддерживает несколько одновременных асинхронных операций ввода-вывода в Windows NT версии 3.5 или более поздней версии, версии AIX5 или более поздней версии или десятой версии Solaris. После версии он начинает поддерживать .

Если я скажу это прямо, вы можете не понять эту концепцию. Временно известно, что под Windows обратите внимание на отправку вызовов ввода-вывода в системное ядро ​​и получение завершенных операций ввода-вывода от ядра через IOCP с циклами событий для завершения процесса асинхронного ввода-вывода. Этот процесс реализован через epoll под linux, который реализован самой libuv.

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

Пул потоков

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

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

Где в этой статье используются пулы потоков: в Node, на платформах *nix или Window. Пулы потоков используются для внутреннего выполнения задач ввода-вывода.

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

  • операции с файловой системой

  • Функции DNS (getaddrinfo и getnameinfo)

  • Пользовательский код добавлен через uv_queue_work()

Между узлом и нижним слоемАсинхронный ввод-выводпроцесс вызова

Сравните два классических кода API на рисунке (server.listenа такжеfs.open, причина выбора двух API: представитель сетевого ввода-вывода и представитель файлового ввода-вывода) и предыдущее изображение libuv, давайте вместе разберемся с процессом вызова асинхронного ввода-вывода

На приведенном выше рисунке показан поток деталей libuv.Код на рисунке очень простой, включая 2 части:

  1. server.listen() — это код, который обычно выполняется на последнем этапе создания TCP-сервера. В основном укажите порт, на котором работает сервер и функцию обратного вызова.

  2. fs.open() открывает файл асинхронно.

Было легко выбрать два примера, потому что схема архитектуры libuv видна: libuv использует разные механизмы для сетевого ввода-вывода и файлового ввода-вывода.

Правая половина приведенного выше рисунка в основном разделена на две части:

  1. Основной поток: основной поток также готов к выполнению при запуске узла. Когда узел запустится, он выполнит серию действий по инициализации, запустит двигатель V8 и войдет в следующий цикл.

  2. Пул потоков: количество пулов потоков можно настроить с помощью переменной среды UV_THREADPOOL_SIZE, максимальное значение — 128, по умолчанию — 4.

Классический метод вызова кода в Node.js: все модули ядра Node вызываются из JavaScript, модули ядра вызывают встроенные модули C++, а встроенные модули выполняют системные вызовы через libuv. Пожалуйста, помните это

цикл событий

не важно какserver.listenещеfs.open, когда они запускают службу узла (процесс), узел создает цикл while(true), который является циклом событий. Каждый раз, когда выполняется тело цикла, мы называем его Tick. Процесс каждого тикаПроверить, есть ли ожидающее событие, и если да, удалите событие и связанную с ним функцию обратного вызова. Если есть связанная функция обратного вызова, она будет выполнена. Затем войдите в следующий цикл, если обработки событий больше нет, выйдите из процесса.

Здесь мы знаем, что цикл событий был создан, см. жирный шрифт вышеОжидается ли событие, где я могу это проверить? Как события попадают в цикл событий? Что будет с событием продолжайте смотреть вниз.

Низкоуровневые вызовы и генерация событий

Продолжайте смотреть на эту картину и объяснить основной процесс генерации событий. (Обратите внимание, что между сетью I / O и файлом ввода / вывода будут некоторые различия). Вот краткое упоминание о вызовах кода C ++, и заинтересованные партнеры могут продолжаться учиться в глубине.

File I/O

(Здесь использовались ранее упомянутые знания о классификации модулей) сначала код javascript, затем вызовlib/fs.jsкод основного модуляfs.open, основной модуль вызывает встроенный модуль C++src/node_file.cc, код встроенного модуля C++ будет иметь оценку платформы, а затем выполнять системный вызов через libuv.

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

После сборки объекта запроса он отправляется в пул потоков ввода-вывода, созданный в libuv.После завершения операции ввода-вывода в пуле потоков полученный результат будет сохранен в атрибуте req->result, а затем функция будет уведомлена.IOCP, который сообщает текущему объекту, что операция завершена.

Во время всего этого процесса в цикле событий, созданном в начале процесса, есть наблюдатель ввода-вывода, который каждый раз, когда выполняется Tick, вызывает метод, связанный с IOCP, чтобы проверить, есть ли завершенный запрос в пуле потоков. Расскажите об объекте запроса и ранее связанном свойстве результата, добавьте его в очередь наблюдателя ввода-вывода, а затем обработайте его как событие.

Видя это, есть ли какое-то событие, которое нужно обработать в вышеупомянутом **, где я могу его проверить? Как события попадают в цикл событий? ** Вы понимаете эти два вопроса?

Текст с картинками. яснее!

Network I/O

Двигатель V8 работает отserver.listen()начни, звониbuiltin module Tcp_wrapпроцесс.

В процессе создания TCP-линка libuv принимает непосредственное участиеTcp_wrap.ccв функцииTCPWrap::listen()Вызовите uv_listen(), чтобы начать выполнениеuv_io_start()конец. Это выглядит как недолговечный процесс, но на самом деле это механизм обработки прерываний, аналогичный ядру Linux.

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

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

Асинхронный ввод-вывод обеспечивает высокую производительность Node.js

традиционная серверная модель

  • Синхронный: синхронный сервис, в котором одновременно может обрабатываться только один запрос, а остальные запросы ожидают.
  • На процесс/на запрос: запускайте процесс для каждого запроса, это может обрабатывать несколько запросов, но не масштабируется, системные ресурсы ограничены, не подходит для запуска слишком большого количества процессов.
  • На поток/на запрос: запускает поток для каждого обрабатываемого запроса. Хотя потоки легче процессов, каждый поток также будет занимать определенный объем памяти, а при наличии больших одновременных запросов он также будет занимать много памяти, что приведет к замедлению работы сервера.

узел другой!

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

В настоящее время Nginx использует тот же метод управления событиями, что и Node, и те, кто заинтересован, также должны узнать об этом, но Nginx написан на языке C.

Подписывайтесь на меня

Об авторе: koala, сосредоточив внимание на совместном использовании полного стека технологий Node.js, от JavaScript до Node.js, до серверной базы данных, я желаю вам стать отличным старшим инженером Node.js. [Руководство по развитию программиста] Автор, блог Github с открытым исходным кодомGitHub.com/koala-co Nth…

  • Добро пожаловать, чтобы добавить меня на WeChat 【ikoala520 】, привлекать васNode.jsПродвинутая продвинутая группа, долгосрочный обмен и обучение...
  • Добро пожаловать, чтобы обратить внимание на «Руководство по развитию программиста на севере», общедоступную учетную запись, которая поможет вам расти с душой...

Ссылаться на

Большая часть содержания этой статьи взята из книги г-на Пу Линга «Введение в Node.js». Хотя эта книга была опубликована уже давно, она все еще заставляет меня чувствовать себя все более и более благоухающим. Я могу расширить и рекомендовать ее, пока чтение.

Libuv Mountining - обработка документовzhuanlan.zhihu.com/p/97789391

Обзор идей дизайна высокопроизводительной библиотеки моделей асинхронного ввода-вывода libuv.blog.CSDN.net/Ах папа не 12345…