Async Queue — ограничение конкурентности
Очередь на входе сервера: лимит одновременных задач + лимит ожидающих. Парадокс — ограничение ускоряет общую latency.
Что это
Под нагрузкой Node «съедает всё что дают» и впадает в degradation: GC давится, event loop задыхается, latency растёт. Решение — поставить async queue на вход и ограничить:
- concurrency — сколько задач одновременно в обработке;
- size — сколько задач может ждать в очереди;
- timeout — максимальное время в очереди.
Три ресурса, которые истощаются
- CPU — слишком много синхронной работы → event loop забит.
- Memory — GC начинает тормозить (не сама нехватка, а GC).
- Open handles — сокеты, file descriptors, DB connections.
API / Пример
class AsyncQueue {
constructor({ concurrency, size }) {
this.concurrency = concurrency;
this.size = size;
this.running = 0;
this.waiting = ;
}
add(task) {
return new Promise((resolve, reject) => {
if (this.waiting.length >= this.size) return reject(new Error('overflow'));
this.waiting.push({ task, resolve, reject });
this.next();
});
}
next {
if (this.running >= this.concurrency || !this.waiting.length) return;
const { task, resolve, reject } = this.waiting.shift();
this.running++;
task.then(resolve, reject).finally(() => { this.running--; this.next(); });
}
}
Производительность / Подводные камни
- Ограничение ускоряет latency — звучит парадоксально, но без очереди event loop захлёбывается, и каждый запрос длится дольше.
- GC тормозит, а не нехватка памяти — на большом heap V8 пытается пройти всё, что замедляется в разы.
- Performance Hooks для авто-тюнинга — измеряют latency и подстраивают concurrency.
- Не делать control flow через EventEmitter — порядок и ошибки трудно отслеживать; промисы лучше.
- await блокирует функцию, не event loop — другие async-задачи продолжают исполняться параллельно.
- TechEmpower бенчмарки — JavaScript обгоняет многие быстрые языки на HTTP-микробенчах.
🎓 Источники
-
🎓 Node.js и Backend Семинар #1 — асинхронная очередь, бенчмарк фреймворков · 2021-12-18
- Тезисы: три ресурса (CPU/Memory/Handles); GC — главный тормоз, не нехватка памяти; ограничение конкурентности улучшает latency; Performance Hooks для авто-тюнинга; await блокирует функцию, не event loop; пассивный WebSocket съедает десятки КБ; не отключать неактивные сокеты — лучше ограничить новые.
- Цитата: «Ограничение конкурентности улучшает latency — звучит парадоксально, но без очереди event loop захлёбывается.»
-
🎓 Concurrent Asynchronous Queue in JavaScript · 2019-05-07
- Тезисы: базовая структура очереди; обработка ошибок; cancel/timeout.