Event loop в Node.js и libuv
↑ 6.1 Node.js изнутри · → Следующая
Зачем это на собесе
Главный вопрос по Node.js: почему один поток обслуживает тысячи запросов и что блокирует event loop.
Объяснение
Node.js выполняет JavaScript в одном главном потоке. Ввод-вывод делегируется операционной системе и пулу потоков libuv, а результат возвращается в главный поток через очереди событий.
Фазы цикла (упрощённо):
| Фаза | Что выполняется |
|---|---|
| timers | колбэки setTimeout, setInterval |
| pending callbacks | отложенные системные колбэки |
| poll | новые события ввода-вывода, выполнение их колбэков |
| check | колбэки setImmediate |
| close callbacks | закрытие сокетов и дескрипторов |
Между колбэками выполняются микрозадачи: сначала process.nextTick, затем очередь промисов (then, await).
console.log("1 sync");
setTimeout(() => console.log("4 timeout"), 0);
setImmediate(() => console.log("5 immediate"));
Promise.resolve().then(() => console.log("3 promise"));
process.nextTick(() => console.log("2 nextTick"));
// порядок: 1, 2, 3, затем 4/5 (порядок timeout и immediate в главном модуле не гарантирован)Пул потоков libuv (по умолчанию 4, UV_THREADPOOL_SIZE) обслуживает операции без асинхронного API в ОС: файловая система, DNS-lookup, часть криптографии, zlib. Сетевой ввод-вывод идёт через epoll/kqueue/IOCP без потоков.
Нюансы и подводные камни
- Тяжёлое вычисление в JS-коде блокирует весь процесс: все запросы ждут (
JSON.parseогромного тела,crypto.pbkdf2Sync, цикл на миллиард итераций). - Синхронные API (
fs.readFileSync) допустимы только при старте. - Бесконечная рекурсия
process.nextTick«голодит» ввод-вывод. - Node.js масштабируют процессами (cluster, контейнеры) и worker threads (см. Worker threads и cluster).
- Нагрузка измеряется задержкой event loop (
perf_hooks.monitorEventLoopDelay).
Практика
- Заблокируйте event loop циклом и измерьте задержку ответа другого эндпоинта.
- Предскажите порядок вывода для смеси
setTimeout,setImmediate, промисов иnextTick. - Замерьте влияние
UV_THREADPOOL_SIZEна параллельное хэширование.
Вопросы с ответами
Как Node.js обслуживает много соединений одним потоком?
Неблокирующий ввод-вывод: запросы регистрируются в ОС, а event loop выполняет колбэки по готовности событий.
Чем nextTick отличается от промисов?
nextTickвыполняется раньше очереди промисов, сразу после текущей операции.
Что блокирует event loop?
Долгий синхронный код в главном потоке.