Node.js a event loop

Node.js a event loop

Co je Node.js a proč event-loop

Node.js je běhové prostředí pro JavaScript mimo prohlížeč, postavené na V8 (JIT kompilátor od Googlu) a nativní knihovně libuv pro asynchronní I/O. Klíčovým principem je jednovláknový event-loop, který multiplexuje tisíce I/O operací bez blokování vlákna JavaScriptu. Výsledkem je vysoká propustnost při nízké latenci u I/O-těžkých služeb (API, proxy, realtime push), při zachování jednoduchého programovacího modelu.

Stavební kameny: V8, libuv, bindings a standardní knihovna

  • V8: parsuje JS, optimalizuje a JIT kompiluje horké úseky kódu, spravuje garbage collector (GC) a heap.
  • libuv: poskytuje smyčku událostí, neblokující síťové I/O, časovače, filesystem přes thread-pool, signály a IPC.
  • Bindings: lepidlo mezi JavaScriptem a C/C++ vrstvou (N-API, node-addon-api) pro volání nativních funkcí.
  • Node standardní knihovna: moduly jako fs, net, http/https, crypto, stream, worker_threads, postavené nad libuv a V8.

Event-loop: fáze a pořadí zpracování

Event-loop v Node.js je logická smyčka řízená libuv, která postupně prochází fáze. Každá fáze má frontu úloh (macrotasks). Zjednodušené pořadí:

  1. Timers: zpracování setTimeout/setInterval, jejichž threshold vypršel.
  2. Pending Callbacks: nízkoúrovňové systémové zpětné volání (např. některé chyby z TCP).
  3. Idle/Prepare: interní fáze libuv.
  4. Poll: hlavní část – přijímá I/O události, obsluhuje sockety, čte/zapisuje data; pokud je fronta prázdná, může blokovat do příchodu události (s výjimkami).
  5. Check: provádí se setImmediate callbacky.
  6. Close Callbacks: například 'close' události socketů/handlů.

Mezi jednotlivými fázemi se vždy vyprazdňuje mikrofronta (microtasks), tj. process.nextTick a Promise .then()/await pokračování.

Microtasks vs. macrotasks: jemná pravidla pořadí

  • Microtasks: vykonají se po každém callbacku a před přechodem do další fáze. Patří sem Promise reakce a process.nextTick. nextTick má navíc přednost i před Promise microtasks – nadměrné používání může vyhladovět event-loop.
  • Macrotasks: položky ve frontách jednotlivých fází (timers, I/O, check…). setTimeout a setImmediate mohou mít různé pořadí podle toho, v jaké fázi byly naplánovány.

Časování: setTimeout vs. setImmediate vs. nextTick

  • setTimeout(fn, 0): minimální zpoždění je orientační (clamping), callback poběží v timers fázi po vypršení prahu a až po microtasks.
  • setImmediate(fn): callback v check fázi hned po poll. Pokud se naplánuje z I/O callbacku, typicky předběhne setTimeout(..., 0).
  • process.nextTick(fn): microtask s nejvyšší prioritou; používejte střídmě (např. pro kompatibilitu API), jinak risk hladovění.

Asynchronní I/O a thread-pool libuv

Sockety a většina síťového I/O jsou skutečně neblokující a řízené pollerem OS (epoll/kqueue/IOCP). Filesystémové operace (část fs) se provádějí v libuv thread-poolu (výchozí velikost bývá 4, nastavit lze přes UV_THREADPOOL_SIZE). Proto může intenzivní práce s diskem blokovat dostupná vlákna a zvyšovat latenci – sledujte fronty a případně navyšujte velikost poolu nebo používejte streaming a batching.

Streams a zpětný tlak (backpressure)

stream.Readable/Writable implementují tokové API s řízením rychlosti. Klíčové je respektovat návratovou hodnotu write() a čekat na 'drain'. Tím se vyhnete přetečení bufferů a stabilizujete latenci. Pipes (readable.pipe(writable)) backpressure řeší automaticky.

Programovací model: callbacky, Promise a async/await

  • Callbacky (historicky error-first kontrakt (err, data)) – stále v některých API (fs).
  • Promise a async/await: moderní API (fs/promises, timers/promises) s přehledným řízením chyb přes try/catch. Pozor na paralelizaci: await v cyklu serializuje; použijte Promise.all/allSettled.

Garbage collector a výkon

V8 používá generacionální GC. Dlouhé pauzy snižují propustnost. Opatření: vyhýbejte se masivní alokaci v horkých smyčkách, recyklujte objekty tam, kde dává smysl, preferujte Buffer pooling u I/O, měřte heap pomocí inspector a heap snapshots. Pro latenci kritické služby je vhodné sledovat event-loop delay a GC statistiky.

Moduly: CommonJS vs. ECMAScript Modules

Node podporuje CJS (require, module.exports) i ESM (import/export). Volba režimu vychází z package.json ("type": "module") a přípon (.mjs/.cjs). Míšení formátů vyžaduje edge pravidla (např. createRequire), proto preferujte jednotný styl v projektu a explicitní exportní mapy (exports).

Worker Threads a Cluster

  • Worker Threads: skutečná paralelizace CPU-těžkých úloh v rámci jednoho procesu s izolovanými heapy, sdílenou pamětí (SharedArrayBuffer) a message-passing. Používejte pro kompresi, šifrování, transformace.
  • Cluster: více procesů sdílejících jeden port (round-robin). Zvyšuje propustnost na vícejádrových serverech, ale každý proces má vlastní heap a event-loop.

Nativní doplňky (Addons) a N-API

Pro kritické úseky lze psát nativní moduly v C/C++ přes N-API, které stabilizuje ABI napříč verzemi Node. Hodí se pro obálky knihoven OS, kryptografii, parsování či těžké výpočty. Dohlédněte na přenositelnost, správu paměti a thread-safety.

HTTP server a síťové vzory

Server http/http2 je event-driven: každé spojení je socket s obsluhou request/response jako streamů. Optimalizace: keep-alive, connection reuse, header packing, podpora HTTP/2, compression (zlib/brotli), správné cache-control a ETag. Zvažte obranu WAF-em, rate limiting, časové limity (headersTimeout, requestTimeout), limit body a ochranu proti slow-loris.

Bezpečnost: runtime i závislosti

  • Závislosti skenujte (audit), zamykejte verze (lockfile), minimalizujte attack surface.
  • Chraňte se proti Prototype Pollution, SSRF, ReDOS (vyhnout se patologickým regexům), XSS v šablonách a path traversal.
  • Aktivujte OpenSSL bezpečné křivky/šifry, validujte certifikáty a hostname při outbound spojeních.

Observabilita: metriky, logy a tracing

Měřte event-loop lag, počet otevřených handlerů, využití heapu/CPU, latence endpointů, chybovost. Pro tracing použijte OpenTelemetry a W3C Trace Context. Logy strukturovaně (JSON), s korelačními ID a ochranou osobních údajů.

Diagnostika a profilace

  • Inspector (--inspect, DevTools) pro step-debugging a profiling CPU/heap.
  • perf/0x/clinic pro plamenové grafy a hledání hotspotů.
  • Async Hooks pro sledování životního cyklu asynchronních zdrojů (opatrně – výkonnostní dopad).

Správa procesu: signály, fronty a vypínání

Implementujte graceful shutdown: odchytávejte SIGTERM, uzavřete příjem nových spojení, počkejte na rozpracované požadavky, uvolněte zdroje a ukončete proces s kódem 0. V kontejnerovém prostředí zohledněte liveness/readiness sondy a back-pressure od ingress vrstvy.

Výkonnostní zásady a anti-patterny

  • Neprovádějte CPU-těžké smyčky v hlavním vlákně – použijte Worker Threads nebo offload na služby.
  • Vyhněte se sync API (fs.readFileSync) v request-pathu.
  • Preferujte streamy před načítáním celého obsahu do paměti.
  • Batchujte a coalesce I/O, využívejte connection pooling.
  • Měřte, profilujte, iterujte – odhad není metrika.

Správa balíčků a nasazení

package.json definuje skripty (start, build), exportní mapy, typ modulu, engines a bin. Pro rychlé starty používejte prebuild, tree-shaking, minimalizujte runtime transpilační kroky. V produkci zamkněte lockfile, zapněte NODE_ENV=production, hlídejte velikost image a nastavte limity paměti.

Kompatibilita a verze Node

Volte LTS řadu pro produkci. Sledujte semver major změny (např. default ESM chování, nová API v fetch, stabilizace test runner). Testujte s více verzemi CI maticí a definujte minimální podporované verze v engines.

Typické architektonické vzory

  • API Gateway/Backend-for-Frontend: agregace dat, cache, rate limit, autorizace.
  • Realtime: WebSocket/SSE, pub-sub, škálování přes Redis/Cloud pubsub.
  • Job workers: fronty (BullMQ, RabbitMQ), idempotence, deduplikace a plánování.

Závěr

Node.js kombinuje jednovláknový event-loop s neblokujícím I/O a mocnou standardní knihovnou. Pochopení fází smyčky, rozdílu microtasks/macrotasks, role thread-poolu a práce se streamy je zásadní pro stabilní a rychlé služby. Správně navržená architektura – s observabilitou, bezpečností, worker threads pro CPU, a pečlivým shutdownem – umožní škálovat od jednoduchých API po globální realtime platformy.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *