#node.js #async-await #event-loop #single-threaded #worker-pool
Вопрос:
Я изучал, как Node JS повышает производительность для нескольких одновременных запросов! Прочитав пару блогов, я узнал, что:
- При поступлении любого запроса запускается событие, и соответствующая функция обратного вызова помещается в очередь событий.
- Цикл событий(Основной поток) отвечает за обработку всех запросов в очереди событий. Цикл событий обрабатывает запрос и отправляет ответ, если запрос использует неблокирующий ввод-вывод.
- Если запрос содержит Блокирующий цикл событий ввода-вывода, он внутренне назначает этот запрос простаивающему работнику из рабочего пула, и когда работник отправляет ответ, цикл событий результата отправляет ответ.
Мой вопрос в том, что, поскольку цикл событий передает тяжелую блокирующую работу внутренне в рабочий пул с использованием библиотеки libuv, зачем нам нужен асинхронный обратный вызов?
Для лучшего понимания, пожалуйста, ознакомьтесь с приведенным ниже кодом:
const express = require('express')
const app = express()
const port = 3000
function readUserSync(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime miliseconds >= new Date().getTime()) {
}
return "User"
}
async function readUserAsync(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime miliseconds >= new Date().getTime()) {
}
return "User"
}
app.get('/sync', (req, res) => {
const user = readUserSync(80)
res.send(user)
})
app.get('/async', async (req, res) => {
const user = await readUserAsync(80)
res.send(user)
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Я проверил производительность для обеих конечных точек с помощью инструмента apache benchmark, предполагая, что каждая операция ввода-вывода занимает 80 мс.
ab -c 10 -t 5 "http://127.0.0.1:3000/async/"
ab -c 10 -t 5 "http://127.0.0.1:3000/sync/"
И, что удивительно, для конечной точки с асинхронным обратным вызовом было больше запросов в секунду.
Итак, как цикл событий, пул потоков и асинхронное ожидание работают внутренне для обработки большего количества одновременных запросов?
Комментарии:
1. В чем именно разница? Они должны быть довольно близко друг к другу, да?
2. Блокировка, отнимающая много времени кода внутри
async
функции, никому не приносит пользы. Это все еще просто блокирующий, отнимающий много времени код, независимо от того, находится ли он вasync
функции или в обычной функции. В случае, если у вас было какое-то ошибочное представление о том, что Javascript вasync
функциях выполняется в другом потоке, это НЕ так. Они выполняются в одном потоке, поэтому блокировка кода Javascript вasync
по-прежнему блокирует цикл событий.3. @jfriend00 Предполагает
readUserSync
иreadUserAsync
выполняет операции ввода-вывода(например, получение пользователя из базы данных mysql), тогда каков будет сценарий для двух конечных точек?4. Это совершенно другой сценарий. Если вы выполняете действительно неблокирующие асинхронные операции ввода-вывода, то они не блокируют цикл событий, как в примерах, которые вы показываете здесь. Несинхронная версия ваших функций, которая фактически не блокирует ввод-вывод, должна будет использовать обратный вызов или событие для связи о завершении, поскольку они будут неблокирующими и вернутся ДО выполнения операций ввода-вывода.