Массовая популярность термина «асинхронная» была в волне Web 2.0, которая охватила Интернет с JavaScript и Ajax. Но асинхронно редко встречается на большинстве языков программирования высокого уровня. PHP лучше всего отражает эту функцию: она не только асинхронно блокирует, но и не предоставляет несколько потоков. PHP выполняется в синхронной блокирующей манере. Такие преимущества полезны для программистов, чтобы записать бизнес -логику в последовательности, но в сложных сетевых приложениях блокировка приводит к тому, что она не станет более одновременной.
На стороне сервера ввод/вывод очень дорогой, а распределенный ввод -вывод дороже. Только когда бэкэнд может быстро реагировать на ресурсы, может стать лучше. Node.js - первая платформа, которая использует асинхронную в качестве основного метода программирования и концепции дизайна. В сопровождении асинхронного ввода-вывода, управляемых событиями и однопоточным, они образуют тон узла. Эта статья представит, как узел реализует асинхронные ввода -вывода.
1. Основные понятия
«Async» и «non blocking» звучат одно и то же, и с точки зрения фактических результатов, оба достигают цели параллелизма. Но с точки зрения компьютерного ядра ввода/вывода, есть только два способа: блокировка и не блокировка. Таким образом, асинхронное/синхронное и блокирование/неблокирование на самом деле две разные вещи.
1.1 Блокировка ввода-вывода и неблокирующий ввод-вывод
Одна особенность блокировки ввода -вывода заключается в том, что после вызова вы должны подождать, пока все операции не будут завершены на уровне ядра системы до завершения вызова. Принимая чтение файла на диске в качестве примера, этот вызов заканчивается после того, как ядро системы завершает поиск диска, считывает данные и копирует данные в память.
Блокировка ввода -вывода заставляет процессор ждать ввода/вывода, тратить время ожидания, а мощность обработки процессора не может быть полностью использована. Характеристика не блокирующего ввода/вывода заключается в том, что он вернется сразу после вызова, и срез времени процессора может использоваться для обработки других транзакций после возврата. Поскольку полный ввод -вывод не завершен, то, что немедленно возвращается, является не данными, ожидаемыми бизнес -уровнем, а только статус текущего вызова. Чтобы получить полные данные, приложение должно многократно вызывать операцию ввода/вывода, чтобы подтвердить, завершены ли они (то есть опрос). Методы опроса нуждаются в следующем:
1. Читать: проверка статуса ввода/вывода с помощью повторяющихся вызовов является наиболее оригинальным и самым низким методом производительности
2. Выберите: Улучшения для чтения, оценить статус события в дескрипторе файла. Недостатком является то, что максимальное количество дескрипторов файлов ограничено.
3. POLL: улучшения выбора, используя связанные списки, чтобы избежать максимального ограничения числа, но когда есть много дескрипторов, производительность все еще очень низкая
4.Epoll: если во время опроса не будет проверено событие ввода -вывода, он будет спать до тех пор, пока событие не произойдет, и разбудит его. Это самый эффективный механизм уведомления о событиях ввода/вывода в рамках Linux.
Опрос отвечает необходимости не блокировки ввода-вывода для обеспечения полного сбора данных, но для приложений он все еще может считаться своего рода синхронизацией, потому что ему все еще нужно ждать, пока ввод-вывод вернется полностью. Во время ожидания процессор используется либо для прохождения состояния дескриптора файла, либо в сгибатель ожидания событий.
1.2 Асинхронный ввод/вывод в идеале и реальности
Идеальным асинхронным вводом/выводом должен быть приложение, которое инициирует неблокирующий вызов, и может напрямую обрабатывать следующую задачу без опроса, просто передайте данные в приложение через сигнал или обратный вызов после завершения ввода-вывода.
В действительности, асинхронный ввод -вывод имеет разные реализации в различных операционных системах. Например, *Nix Platform принимает пользовательский пул потоков, в то время как Windows Platform принимает модель IOCP. Узел предоставляет LIBUV в качестве абстрактного уровня инкапсуляции для инкапсуляции суждений о совместимости платформы и гарантирует, что реализация асинхронного ввода/вывода верхнего узла и нижних платформ является независимой. Следует подчеркнуть, что мы часто упоминаем, что узел однопоточный, что только означает, что выполнение JavaScript находится в одном потоке, и есть другие пулы потоков, которые фактически выполняют задачи ввода-вывода в узле.
2. Асинхронный ввод -вывод узла
2.1 Петля событий
Модель выполнения узла на самом деле является цикла событий. Когда процесс запускается, узел создает бесконечную петлю, и каждый процесс выполнения корпуса петли становится клещей. Каждый процесс тика должен проверить, есть ли события, ожидающие обработки. Если это так, события и связанные с ними функции обратного вызова будут удалены. Если есть связанные функции обратного вызова, они будут выполнены, а затем будет введен следующий цикл. Если больше нет обработки событий, выйдите из процесса.
2.2 Наблюдатель
В каждом цикле событий есть несколько наблюдателей, и, спросив этих наблюдателей, мы можем определить, есть ли события, которые должны быть обработаны. Цикл событий является типичной моделью производителя/потребителя. В узле события в основном поступают из сетевых запросов, ввода/вывода файлов и т. Д.
2.3 Запросить объект
Во время перехода от JavaScript к ядру, выполняющему операции ввода -вывода, существует промежуточный продукт, называемый объектом запроса. Принимая простейший метод fs.open () в Windows (откройте файл и получите дескриптор файла в соответствии с указанным путем и параметрами) в качестве примера, от вызовов JS до встроенных модулей, система вызовов через LIBUV фактически называется методом UV_FS_OPEN (). Во время процесса вызова создается объект запроса FSREQWrap, а в этом объекте запроса инкапсулируются параметры и методы, передаваемые из уровня JS. Функция обратного вызова, которой мы больше всего обеспокоены, устанавливается на свойство ONCOMPETE_SYM этого объекта. После того, как объект завершится, вставьте объект FSREQWrap в пул потоков и дождитесь выполнения.
На этом этапе вызов JS возвращается немедленно, и поток JS может продолжать выполнять последующие операции. Текущая операция ввода/вывода ждет выполнения в пуле потоков, который завершает первый этап асинхронного вызова.
2.4 выполнить обратные вызовы
Уведомление о обратном вызове - это вторая фаза асинхронного ввода -вывода. После того, как операция ввода/вывода в пуле потоков будет вызвана полученными результатами, и затем IOCP уведомляется, что операция текущего объекта была завершена, а поток возвращает пул потоков. Во время каждого выполнения тика наблюдатель ввода -вывода в цикле событий вызовет соответствующий метод, чтобы проверить, есть ли заполненные запросы в пуле потоков. Если он существует, объект запроса будет добавлен в очередь наблюдателя ввода/вывода, а затем обрабатывается как событие.
3. не асинхронный API.
Существуют также некоторые асинхронные API, которые не связаны с вводом/выводом в узле, таких как Timers setTimeout (), setInterval (), process.nexttick () и setimmdiate (), которые немедленно выполняют задачи асинхронно и т. Д., Которые будут кратко введены здесь.
3.1 Таймер API
API на стороне браузера SetTimeout () и SetInterval () согласованы. Их принцип реализации аналогичен асинхронному вводу/выводу, но они не требуют участия пула потоков ввода/вывода. Таймер, созданный вызовом The Timer API, будет вставлен в красное и черное дерево внутри наблюдателя таймера. Тик каждого цикла событий выведет объект таймера из красного и черного дерева, чтобы проверить, превысило ли время времени. Если он превысит, будет сформировано событие, и функция обратного вызова будет выполнена немедленно. Основная проблема с таймером заключается в том, что его время времени не является особенно точным (миллисекунд, в пределах терпимости).
3.2 API API выполнения асинхронного выполнения задач
До появления узла многие люди могли бы вызвать это, чтобы немедленно выполнить задачу асинхронно:
Кода -копия выглядит следующим образом:
settimeout (function () {
// Тодо
}, 0);
Из -за характеристик петель событий таймер недостаточно точен, а использование красного и черного дерева требует использования таймера, а сложность различного времени работы составляет O (log (n)). Метод Process.nexttick () только поместит функцию обратного вызова в очередь и выведет ее и выполнит в следующем раунде тика. Сложность o (1), и она более эффективна.
Кроме того, существует метод setimmediate (), аналогичный приведенному выше методу, оба задержки выполнения функции обратного вызова. Тем не менее, у первого есть более высокий приоритет, чем последний, потому что цикл событий проверяет наблюдателя в последовательности. Кроме того, первая функция обратного вызова сохраняется в массиве, и каждый раунд тика будет выполнять все функции обратного вызова в массиве; Последний результат сохраняется в связанном списке, и каждый раунд тикания будет выполнять только одну функцию обратного вызова.
4. Серверы, управляемые событиями и высокоэффективные
Предыдущий пример иллюстрирует, как узел реализует асинхронный ввод -вывод. Фактически, Node также применяет асинхронную ввод/вывод для обработки сетевых розетков, что также является основой для узла для создания веб -сервера. Классические серверные модели:
1. Синхронный: только один запрос может быть обработан за раз, а остальные запросы находятся в состоянии ожидания
2. Per -процесс/за запрос: запустите один процесс для каждого запроса, но системные ресурсы ограничены и не имеют масштабируемости.
3. за поток/за запрос: запустите по одному потоку для каждого запроса. Потоки легче процессов, но каждый поток занимает определенное количество памяти. Когда появятся большие одновременные запросы, память скоро закончится.
Знаменитый Apache применяет форму для загрузки/за просмотр, поэтому трудно справиться с высокой параллельностью. Узел обрабатывает запросы с помощью методов, управляемых событиями, которые могут сохранить накладные расходы создания и уничтожения потоков. В то же время операционная система имеет меньше потоков при планировании задач, а стоимость переключения контекста также очень низкая. Узел может обрабатывать запросы упорядоченным образом даже с большим количеством соединений.
Хорошо известный сервер Nginx также отказывается от метода многопоточного и применяет тот же метод, управляемый событиями, как узел. Теперь Nginx в значительной степени заменить Apache. Nginx написан в Pure C и имеет высокую производительность, но он подходит только для веб -серверов, используется для обратного прокси или балансировки нагрузки и т. Д. Узел может создавать те же функции, что и Nginx, а также может обрабатывать различные конкретные предприятия, и его собственная производительность также хороша. В реальных проектах мы можем объединить их для достижения наилучших результатов приложения.