Содержание этой статьи в основном разделено на две части: первая часть — это основа и архитектура Node.js, а вторая — реализация основных модулей Node.js.
- 1 Основа и архитектура Node.js
- Компоненты Node.js
- Архитектура кода Node.js
- Процесс запуска Node.js
- Цикл событий Node.js
- Реализация двух основных модулей Node.js
- процесс и межпроцессное взаимодействие
- Поток и межпотоковое взаимодействие
- Cluster
- Пул потоков Libuv
- обработка сигнала
- документ
- TCP
- UDP
- DNS
1. Состав узла
Node.js в основном состоит из V8, Libuv и сторонних библиотек:
- Libuv: кроссплатформенная библиотека асинхронного ввода-вывода, но она предоставляет функции не только ввода-вывода, но и процессов, потоков, сигналов, таймеров, межпроцессного взаимодействия, пулов потоков и т. д.
- Сторонние библиотеки: асинхронный парсинг DNS (cares), парсер HTTP (http_parser для старой версии, llhttp для новой версии), парсер HTTP2 (nghttp2), библиотека распаковки и сжатия (zlib), библиотека шифрования и дешифрования (openssl), и т.п.
- V8: Реализовать синтаксический анализ, выполнение и поддержку пользовательских расширений JS Благодаря поддержке V8 пользовательских расширений появился Node.js.
2. Структура кода Node.js
На картинке выше показана структура кода Node.js Код Node.js в основном делится на три типа: JS, C++ и C:
- JS — это те модули, которые мы обычно используем (http/fs).
- Код C++ разделен на три части: первая часть инкапсулирует функции Libuv, вторая часть не зависит от Libuv (криптографическая часть API использует пул потоков Libuv), например модуль Buffer, а третья часть это код V8.
- Код уровня языка C в основном инкапсулирует функции операционной системы, такие как TCP и UDP.
Разобравшись с составом и архитектурой кода Node.js, давайте посмотрим, что делает Node.js во время запуска.
3. Процесс запуска Node.js
3.1 Регистрация модулей C++
Во-первых, Node.js вызовет функцию registerBuiltinModules для регистрации модуля C++.Эта функция вызовет серию функций registerxxx.Мы обнаружили, что эти функции нельзя найти в исходном коде Node.js, поскольку эти функции реализованы в каждом C++. модуль через определения макросов.Да, после того, как макрос развернут, это содержимое желтого поля на рисунке выше.Функция каждой функции registerxxx состоит в том, чтобы вставить узел в связанный список модуля C++ и, наконец, связанный список будет сформирован.
Так как же получить доступ к этим модулям C++ в Node.js? В Node.js доступ к модулям C++ осуществляется через internalBinding, логика internalBinding очень проста, то есть найти соответствующий модуль из очереди модулей по имени модуля. Но эту функцию можно использовать только внутри Node.js, а не в пользовательских модулях JS, пользователи могут получить доступ к модулям C++ через process.binding.
3.2 Объект среды и контекст привязки
После регистрации модуля C++ создается объект Environment. Среда — это объект среды при выполнении Node.js. Он действует как глобальная переменная. Он записывает некоторые общедоступные данные Node.js во время выполнения. После создания среды Node.js привяжет объект к контексту V8, зачем это делать? Основная цель - получить объект env в контексте выполнения V8, потому что в V8 есть только такие объекты, как Isolate и Context.Если мы хотим получить содержимое объекта Environment в среде выполнения V8, мы можем получить объект Environment через Context.
3.3 Инициализировать загрузчик модулей
- Node.js сначала проходит через загрузчик модулей C++, выполняет loader.js, а loader.js в основном инкапсулирует загрузчик модулей C++ и собственный загрузчик модулей JS и сохраняет их в объекте env.
- Затем передайте загрузчики модулей C++ и JS и выполните run_main_module.js.
- Передайте обычный JS и собственный загрузчик модулей JS в run_main_module.js для выполнения пользовательского JS.
Предположим, что пользовательский JS выглядит следующим образом:
- require('net')
- require('./myModule')
Пользовательский модуль и нативный модуль JS загружаются соответственно.Давайте посмотрим на процесс загрузки.При выполнении require:
- Node.js сначала определит, является ли он собственным JS-модулем, если нет, он напрямую загрузит пользовательский модуль, в противном случае он будет использовать собственный загрузчик модулей для загрузки собственного модуля JS.
- При загрузке собственных модулей JS, если используются модули C++, используйте internalBinding для их загрузки.
3.4 Выполнение пользовательского кода, цикл событий Libuv
Затем Node.js выполнит JS пользователя. Обычно JS пользователя генерирует задачи для цикла событий, а затем входит в систему цикла событий. Например, когда мы слушаем сервер, мы создаем новый дескриптор TCP в цикл событий. Node.js всегда будет работать в этом цикле событий.
net.createServer(() => {}).listen(80)
4. Цикл событий
Давайте посмотрим на реализацию цикла событий. Цикл событий в основном разделен на 7 этапов. Этап таймера в основном связан с задачами, связанными с таймером. Этап ожидания в основном имеет дело с обратными вызовами, созданными в обратном вызове этапа ввода-вывода опроса. Этапы проверки, подготовки и простоя являются пользовательскими этапами. три этапа Будут выполняться задачи каждого цикла последовательности событий.Этап ввода-вывода опроса в основном касается таких задач, как сетевой ввод-вывод, сигналы, пулы потоков и т. д. Этап закрытия в основном касается закрытых дескрипторов, таких как выключение сервера.
- этап таймера: реализован с помощью двоичной кучи, самый быстрый срок действия истекает на корневом узле.
- ожидающий этап: обработка обратных вызовов, сгенерированных в обратных вызовах этапа ввода-вывода опроса.
- фазы проверки, подготовки, простоя: каждый раз, когда выполняется цикл обработки событий.
- Фаза ввода-вывода опроса: обработка событий, связанных с файловым дескриптором.
- фаза закрытия: выполнить обратный вызов, переданный при вызове функции uv_close.
Ниже мы подробно рассмотрим реализацию каждого этапа.
4.1 Фаза таймера
Базовая структура данных таймера представляет собой двоичную кучу с самым быстро истекающим узлом наверху. На этапе таймера он будет проходить узел за узлом.Если время ожидания узла истекло, будет выполнен его обратный вызов.Если времени ожидания нет, то последующим узлам не нужно будет судить, потому что текущий узел истекает быстрее всего, если он all Срок действия не истек, что указывает на то, что срок действия других узлов также не истек. После выполнения обратного вызова узла он будет удален.Для поддержки сценария setInterval, если установлен флаг повторения, узел будет повторно вставлен в бинарную кучу.
Мы видим, что базовая реализация немного проще, но реализация модуля таймера Node.js немного сложнее.
- Node.js поддерживает двоичную кучу на уровне JS.
- Каждый узел кучи поддерживает связанный список, и в этом связанном списке узел с наибольшим временем ожидания помещается в конец.
- Кроме того, Node.js также поддерживает карту, ключ карты — это относительное время ожидания, а значение — соответствующий узел двоичной кучи.
- Все узлы кучи соответствуют нижнему узлу тайм-аута.
Когда мы вызываем setTimeout, мы сначала находим узел бинарной кучи с карты по входным параметрам setTimeout, а затем вставляем его в хвост связанного списка, при необходимости Node.js будет обновлять нижний слой по самому быстрому время ожидания бинарной кучи js Время ожидания узла. Когда цикл обработки событий обрабатывает стадию таймера, Node.js будет проходить через двоичную кучу JS, затем получать узлы с истекшим сроком действия, а затем проходить связанный список в узлах с истекшим сроком действия, определять, нужно ли выполнять обратный вызов один за другим, и корректировать двоичная куча JS, если необходимо, и базовый тайм-аут.
4.2 проверка, холостой ход, этап подготовки
Этапы проверки, бездействия и подготовки относительно просты.Каждый этап поддерживает очередь, а затем, когда соответствующий этап обрабатывается, выполняется обратный вызов каждого узла в очереди.Однако эти три этапа отличаются тем, что узлы в очереди выполняются, не будут удалены, но останутся в очереди, если не будут удалены явным образом.
4.3 незавершенные и завершающие этапы
ожидающая стадия: обратный вызов, сгенерированный в обратном вызове Poll IO. фаза закрытия: выполните обратный вызов, чтобы закрыть дескриптор. Этапы ожидания и закрытия также поддерживают очередь, а затем выполняют обратный вызов каждого узла на соответствующем этапе и, наконец, удаляют соответствующий узел.
4.4 Этап ввода/вывода опроса
Этап Poll IO — самый важный и сложный этап, давайте посмотрим на реализацию. Во-первых, давайте взглянем на основную структуру данных этапа ввода-вывода опроса: наблюдатель ввода-вывода, наблюдатель ввода-вывода — это инкапсуляция файловых дескрипторов, представляющих интерес событий и обратных вызовов, в основном используемых в epoll.
Когда у нас есть дескриптор файла, который нужно отслеживать с помощью epoll
- Мы можем создать наблюдателя ввода-вывода.
- Вызовите uv__io_start, чтобы вставить очередь наблюдателя ввода-вывода в цикл обработки событий.
- Libuv записывает сопоставление файловых дескрипторов и наблюдателей ввода-вывода.
- На этапе опроса ввода-вывода он будет проходить через очередь наблюдателя ввода-вывода, а затем использовать epoll для выполнения соответствующей обработки.
- При возврате из epoll мы можем получить, какие события дескриптора файла запускаются, и, наконец, найти соответствующего наблюдателя ввода-вывода в соответствии с дескриптором файла и выполнить его обратный вызов.
Кроме того, мы видим, что стадия Poll IO может блокироваться, блокируется ли она и как долго зависит от текущего состояния системы цикла обработки событий. Когда происходит блокировка, чтобы гарантировать, что фаза таймера выполняется вовремя, время блокировки epoll должно быть установлено равным времени самого быстрого истекшего узла таймера.
5. Процессы и межпроцессное взаимодействие
5.1 Создайте процесс
Процесс в Node.js создается в режиме fork+exec, fork — копирование данных основного процесса, exec — загрузка новой программы на выполнение. Node.js предоставляет два режима создания процессов: асинхронный и синхронный.
- Асинхронный способ
Асинхронный метод заключается в том, что после создания дочернего процесса основной процесс и дочерний процесс выполняются независимо, не мешая друг другу. В структуре данных основного процесса, как показано на рисунке, основной процесс будет записывать информацию дочернего процесса, которая будет использоваться при завершении дочернего процесса.
- Синхронно
Синхронное создание дочерних процессов приведет к блокировке основного процесса.Конкретная реализация
- В основном процессе будет создана новая структура цикла событий, а затем на основе этого нового цикла событий будет создан дочерний процесс.
- Затем основной процесс выполняется в новом цикле событий, а старый цикл событий блокируется.
- Когда дочерний процесс завершается, новый цикл событий завершается, таким образом, возвращаясь к старому циклу событий.
5.2 Межпроцессное взаимодействие
Теперь давайте посмотрим, как родительский и дочерний процессы взаимодействуют друг с другом? В операционной системе виртуальные адреса между процессами независимы, поэтому нет возможности напрямую общаться на основе памяти процесса, в этом случае требуется память, предоставляемая ядром. Существует множество способов взаимодействия между процессами, каналами, сигналами, разделяемой памятью и так далее.
Выбор Node.js Процесс коммуникации является доменом UNIX, почему Node.js выбран доменом UNIX? Поскольку только домен UNIX поддерживает доставку файловых дескрипторов, доставка файловых дескрипторов является очень важной возможностью.
Сначала мы смотрим на отношения между файловой системой и процессами в операционной системе, когда процесс открывает файл, он представляет собой формирование fd->file->inode такой связи, что при разветвлении дочерний процесс будет наследоваться.
Но что, если основной процесс открывает файл после разветвления дочернего процесса и хочет сообщить об этом дочернему процессу? Если вы просто передадите номер, соответствующий дескриптору файла, дочернему процессу, дочерний процесс не сможет узнать файл, соответствующий этому номеру. При отправке через домен Unix система также скопирует файловый дескриптор и отношение к файлу в дочерний процесс.
Реализация
- Нижний уровень Node.js создает два файловых дескриптора через socketpair.Основной процесс получает один из файловых дескрипторов и инкапсулирует методы send и on meesage для межпроцессного взаимодействия.
- Затем основной процесс передает другой файловый дескриптор дочернему процессу через переменную среды.
- Подпроцессы также инкапсулируют интерфейс для отправки и получения данных на основе файловых дескрипторов. Таким образом, два процесса могут взаимодействовать.
6. Поток и межпотоковое взаимодействие
6.1 Архитектура потоков
Node.js является однопоточным.Чтобы облегчить пользователям выполнение трудоемких операций, Node.js поддерживает многопоточность после поддержки нескольких процессов. Многопоточная архитектура в Node.js показана на рисунке ниже.Каждый подпоток по существу является независимым циклом обработки событий, но все потоки будут совместно использовать базовый пул потоков Libuv.
6.2 Создание потоков
Далее мы рассмотрим процесс создания потока.
Когда мы вызываем new Worker для создания потока
- Основной поток сначала создаст структуру данных для создания двух сообщений, а затем отправит сообщение узлу для загрузки JS-файла.
- Затем вызовите базовый интерфейс, чтобы создать поток.
- В это время создается дочерний поток.После того, как дочерний поток создан, он сначала инициализирует свою собственную среду выполнения и контекст.
- Затем прочитайте сообщение из структуры данных связи, затем загрузите соответствующий файл js для выполнения и, наконец, войдите в цикл обработки событий.
6.3 Межпоточная связь
Так как же взаимодействуют потоки в Node.js? Поток отличается от процесса.Адресное пространство процесса является независимым и не может взаимодействовать напрямую, но адрес потока является общим, поэтому он может взаимодействовать напрямую на основе памяти процесса.
Давайте посмотрим, как Node.js реализует межпотоковое взаимодействие. Прежде чем узнать о взаимодействии между потоками Node.js, давайте рассмотрим некоторые основные структуры данных.
- Сообщение представляет собой сообщение.
- MessagePortData — это инкапсуляция операции Message и носителя сообщения.
- MessagePort — это конечная точка, представляющая связь и являющаяся инкапсуляцией MessagePortData.
- MessageChannel — это два конца связи, представляющие два MessagePorts.
Мы видим, что два порта связаны друг с другом, когда вам нужно отправить сообщение на противоположный конец, вам нужно только вставить узел в очередь сообщений противоположного конца. Рассмотрим конкретный процесс общения.
- Поток 1 вызывает postMessage для отправки сообщения.
- postMessage сначала сериализует сообщение.
- Затем получите блокировку очереди одноранговых сообщений и вставьте сообщение в очередь.
- После того, как сообщение успешно отправлено, также необходимо уведомить резьбу, где находится приемник сообщения.
- Получатель сообщения будет обрабатывать сообщение на этапе ввода-вывода опроса цикла событий.
7. Cluster
Мы знаем, что Node.js является архитектурой с одним процессом и не может использовать преимущества нескольких ядер.Модуль Cluster позволяет Node.js поддерживать архитектуру сервера с несколькими процессами. Node.s поддерживает два режима: опрос (основной процесс accept) и совместное использование (дочерний процесс accept), которые можно установить с помощью переменных среды. Многопроцессорная серверная архитектура обычно имеет два режима: первый — основной процесс обрабатывает соединение, а затем распределяет его между подпроцессами для обработки, второй — подпроцессы совместно используют сокет и получают соединение для обработки через конкуренцию.
Давайте посмотрим, как используется модуль Cluster.
Это пример использования модуля Cluster.
- Основной процесс вызывает fork для создания дочерних процессов.
- Дочерний процесс запускает сервер. Обычно несколько процессов, прослушивающих один и тот же порт, сообщают об ошибке. Давайте посмотрим, как Node.js справляется с этой проблемой.
7.1 Основной процесс принятия
Давайте сначала посмотрим на режим принятия основного процесса.
- Во-первых, основной процесс разветвляет несколько дочерних процессов для обработки.
- Затем вызовите listen для каждого дочернего процесса.
- При вызове функции listen дочерний процесс отправляет сообщение основному процессу.
- В это время основной процесс создаст сокет, привяжет адрес и переведет его в состояние прослушивания.
- Когда приходит соединение, основной процесс отвечает за получение соединения, а затем за его передачу дочернему процессу путем передачи файлового дескриптора.
7.2 Принятие подпроцесса
Давайте снова посмотрим на режим принятия подпроцесса.
- Во-первых, основной процесс разветвляет несколько дочерних процессов для обработки.
- Затем вызовите listen для каждого дочернего процесса.
- При вызове функции listen дочерний процесс отправляет сообщение основному процессу.
- В это время основной процесс создаст сокет и привяжет адрес. Но он не будет установлен в состояние прослушивания, а сокет будет возвращен дочернему процессу через файловый дескриптор.
- При поступлении соединения оно будет обрабатываться дочерним процессом.
8. Пул потоков Libuv
Зачем вам нужно использовать пул потоков? Файловый ввод-вывод, DNS и задачи с интенсивным использованием ЦП не подходят для обработки в основном потоке Node.js, и эти задачи необходимо помещать в подпотоки для обработки.
Прежде чем понять реализацию пула потоков, давайте взглянем на механизм асинхронной связи Libuv.Асинхронная связь относится к механизму связи между основным потоком Libuv и другими подпотоками. Например, основной поток Libuv выполняет обратный вызов, а подпоток одновременно выполняет задачу, так как же уведомить основной поток, что требует использования механизма асинхронной связи.
- Libuv поддерживает внутреннюю очередь асинхронной связи, когда требуется асинхронная связь, в нее вставляется асинхронный узел.
- В то же время Libuv также поддерживает наблюдатель ввода-вывода, связанный с асинхронной связью.
- Когда асинхронная задача завершена, в поле ожидания соответствующего асинхронного узла будет установлено значение 1, что указывает на завершение задачи. И уведомить основной поток.
- Основной поток выполнит обратный вызов для обработки асинхронной связи на этапе ввода-вывода опроса и выполнит обратный вызов узла, ожидание которого равно 1 в обратном вызове.
Здесь мы рассмотрим реализацию пула потоков.
- Пул потоков поддерживает очередь ожидающих выполнения задач, и несколько потоков берут задачи из очереди для взаимоисключающей обработки.
- Когда задача отправляется в пул потоков, узел вставляется в очередь.
- Когда дочерний поток завершит обработку задачи, он сам вставит задачу в цикл обработки событий, чтобы сохранить завершенную очередь задач, и уведомит основной поток через механизм асинхронной связи.
- Основной поток выполнит обратный вызов, соответствующий задаче, на этапе ввода-вывода опроса.
9. Сигнал
На приведенном выше рисунке представлено представление сигналов в операционной системе.Операционная система использует тип long для представления информации, полученной процессом, и использует массив для обозначения соответствующей функции обработки. Давайте посмотрим, как модуль signal реализован в Libuv.
- В Libuv поддерживается красно-черное дерево, и новый узел вставляется, когда мы прослушиваем новый сигнал.
- При вставке первого узла Libuv инкапсулирует наблюдателя ввода-вывода, зарегистрированного в epoll, чтобы отслеживать, есть ли сигнал для обработки.
- При появлении сигнала находит соответствующий дескриптор из красно-черного дерева по типу сигнала, а затем уведомляет основной поток
- Основной поток будет выполнять обратные вызовы один за другим на этапе ввода-вывода опроса.
В Node.js мониторинг сигналов реализован путем прослушивания события newListener, представляющего собой механизм перехватчиков. Каждый раз, когда вы слушаете событие, если вы слушаете событие newListener, оно вызывает событие newListener. Поэтому, когда выполняется process.on('SIGINT'), вызывается startListeningIfSignal (обработчик события newListener) для регистрации красно-черного узла дерева. И отношения подписки сохраняются в модуле событий.При срабатывании сигнала выполняется process.emit('SIGINT') для уведомления подписчика.
10. Документация
10.1 Файловые операции
Файловые операции в Node.js делятся на синхронный и асинхронный режимы.Синхронный режим заключается в прямом вызове API файловой системы в основном процессе.Этот метод может привести к блокировке процесса.Асинхронный метод использует пул потоков Libuv поставить блокирующие операции.Перейти к дочернему потоку для обработки, а основной поток может продолжить обработку других операций.
10.2 Мониторинг файлов
Мониторинг файлов в Node.js предоставляет два режима, основанных на опросе и публикации по подписке. Давайте сначала посмотрим на реализацию режима опроса. Режим опроса относительно прост. Он реализован с помощью таймера. Node.js будет регулярно выполнять обратный вызов. В обратном вызове сравниваются метаданные текущего файла и последнего Приобретение Если это так, файл был изменен.
Второй режим прослушивания является более эффективным механизмом inotify, inotify основан на режиме публикации подписки, что позволяет избежать недопустимого опроса. Давайте сначала посмотрим на механизм inotify операционной системы Использование inotify и epoll аналогично:
- Сначала получите файловый дескриптор, соответствующий экземпляру inotify через интерфейс.
- Затем управляйте экземпляром inotify, добавляя, удаляя, изменяя и проверяя интерфейс.Например, когда необходимо отслеживать файл, интерфейс вызывается для добавления отношения подписки к экземпляру inotify.
- Когда файл изменяется, мы можем вызвать интерфейс чтения, чтобы узнать, какие файлы были изменены.Inotify обычно используется в сочетании с epoll.
Далее давайте посмотрим, как Node.js реализует мониторинг файлов на основе механизма inotify.
- Во-первых, Node.js инкапсулирует файловый дескриптор и обратный вызов экземпляра inotify в наблюдатель ввода-вывода и регистрирует его в epoll.
- Когда файл необходимо отслеживать, Node.js вызывает системную функцию для вставки элемента в экземпляр inotify и получения идентификатора, затем Node.js инкапсулирует идентификатор и информацию о файле в структуру, а затем вставляет красный и черное дерево.
- Node.js поддерживает красно-черное дерево, и каждый узел красно-черного дерева записывает отслеживаемый файл или каталог и список обратных вызовов при запуске события.
- При срабатывании события на этапе Poll IO будет выполнен соответствующий callback, который определит, какие файлы изменились, а затем найдет соответствующий интерфейс из красно-черного дерева по id и выполнит соответствующий callback.
11. TCP
Обычно мы вызываем http.createServer(cb).listen(port) для запуска сервера, так что же делает этот процесс? Функция listen на самом деле является инкапсуляцией сетевого API:
- Сначала получите розетку.
- Затем привяжите адрес к сокету.
- Затем вызовите функцию прослушивания, чтобы перевести сокет в состояние прослушивания.
- Наконец, зарегистрируйте сокет для epoll и дождитесь установления соединения.
Так как же Node.js обрабатывает соединения? Когда соединение TCP установлено, Node.js выполнит соответствующий обратный вызов на этапе ввода-вывода опроса:
- Node.js вызовет accept, чтобы разорвать TCP-соединение.
- Затем будет вызван уровень C++, и уровень C++ создаст новый объект, представляющий собой экземпляр связи с клиентом.
- Затем вызовите уровень JS, и JS также создаст новый объект для представления экземпляра связи, в основном для пользователей.
- Наконец, зарегистрируйтесь, чтобы дождаться читаемых событий и дождаться, пока клиент отправит данные.
Это процесс Node.js, обрабатывающий соединение. После обработки соединения Node.js определяет, установлен ли флаг single_accept. Если да, он будет спать в течение определенного периода времени и будет обрабатывать оставшиеся соединения для других процессов, чтобы избежать Ответственность в некоторой степени.Сбалансированный, если этот флаг не установлен, Node.js продолжит попытки обработать следующее соединение. Это весь процесс обработки соединений Node.js.
12. UDP
Поскольку UDP - это неподключенный и ненадежный протокол, его относительно просто реализовать и использовать. Вот процесс отправки данных UDP. Когда мы отправляем пакет данных UDP, Libuv сначала вставляет данные в очередь ожидания для отправки, а затем в регистре в epoll, чтобы дождаться события, доступного для записи.Когда событие, доступное для записи, запускается, Libuv будет проходить через очередь ожидания и отправлять его один за другим.После успешной отправки Libuv переместит узел в очередь успешной отправки и вставьте узел в ожидающую стадию.На ожидающей стадии Libuv выполнит вызов к каждому узлу в очереди завершения отправки, чтобы уведомить вызывающую сторону о том, что отправка завершена.
13. DNS
Поскольку API для поиска IP-адреса по доменному имени или поиска доменного имени по IP-адресу является блокирующим, эти две функции реализованы с помощью пула потоков Libuv. При запуске операции поиска Node.js упомянет задачу в пуле потоков, а затем продолжит обработку других вещей.В то же время подпоток пула потоков вызовет базовую функцию для выполнения DNS-запроса. запрос завершен, подпоток отправит результат в основной поток. Это весь процесс поиска.
Другие операции DNS реализуются с помощью Cares, которая представляет собой асинхронную библиотеку DNS.Мы знаем, что DNS — это протокол прикладного уровня, и Cares реализует этот протокол. Давайте посмотрим, как Node.js реализует операции DNS, используя Cares.
- Прежде всего, когда Node.js инициализируется, он инициализирует библиотеку cares, наиболее важной из которых является установка обратного вызова для изменений сокета. Через мгновение мы увидим этот обратный вызов в действии.
- Когда мы инициируем операцию DNS, Node.js вызовет интерфейс cares, интерфейс Cares создаст сокет и инициирует DNS-запрос, а затем передаст сокет Node.js через обратный вызов изменения состояния.
- Node.js регистрирует разъем в Epoll и ждет результата запроса. Когда возвращается результат запроса, Node.js будет вызывать функцию Cares в Parse и, наконец, вызовов обратную запись JS, чтобы уведомить пользователя.
14. Резюме
Эта статья знакомит с реализацией Node.js с общей точки зрения, а также знакомит с реализацией некоторых основных модулей. Из этой статьи мы также увидели много базового контента, Node.js — это среда выполнения JS, созданная путем объединения возможностей V8 и операционной системы. Глубокое понимание принципа и реализации Node.js позволит вам лучше использовать Node.js.
Для получения дополнительной информации см.:GitHub.com/Он и клиенты Анаро/U…