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átoru od Googlu) a nativní knihovně libuv pro asynchronní I/O. Klíčovým principem je jednovláknová smyčka událostí, která multiplexuje tisíce I/O operací bez blokování vlákna JavaScriptu. Výsledkem je vysoká propustnost při nízké latenci u služeb náročných na I/O (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 haldu.
- libuv: poskytuje smyčku událostí, neblokující síťové I/O, časovače, práci se souborovým systémem přes fond vláken, signály a IPC.
- Bindings: propojení mezi JavaScriptem a vrstvou C/C++ (N-API, node-addon-api) pro volání nativních funkcí.
- Standardní knihovna Node.js: 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á knihovnou libuv, která postupně prochází jednotlivými fázemi. Každá fáze má frontu úloh (makroúloh). Zjednodušené pořadí:
- Timers: zpracování
setTimeout/setInterval, jejichž časový limit vypršel. - Pending Callbacks: nízkoúrovňová systémová zpětná volání (např. některé chyby TCP).
- Idle/Prepare: interní fáze knihovny libuv.
- Poll: hlavní část – přijímá I/O události, obsluhuje sockety, čte a zapisuje data; pokud je fronta prázdná, může čekat na příchod události (až na výjimky).
- Check: provádějí se callbacky
setImmediate. - Close Callbacks: například události
'close'u socketů a handlerů.
Mezi jednotlivými fázemi se vždy vyprazdňuje fronta mikroúloh (microtasks), tedy úlohy process.nextTick a pokračování Promise .then()/await.
Mikroúlohy vs. makroúlohy: jemná pravidla pořadí
- Mikroúlohy: vykonají se po každém callbacku a před přechodem do další fáze. Patří sem reakce Promise a
process.nextTick.nextTickmá navíc přednost i před mikroúlohami Promise – jeho nadměrné používání může vyhladovět event loop. - Makroúlohy: položky ve frontách jednotlivých fází (timers, I/O, check…).
setTimeoutasetImmediatemohou 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 se spustí ve fázi timers po uplynutí časového limitu a až po zpracování mikroúloh.setImmediate(fn): callback se spustí ve fázi check hned po fázi poll. Pokud se naplánuje z I/O callbacku, obvykle předběhnesetTimeout(..., 0).process.nextTick(fn): mikroúloha s nejvyšší prioritou; používejte ji střídmě (např. kvůli kompatibilitě API), jinak hrozí vyhladovění event loopu.
Asynchronní I/O a fond vláken libuv
Sockety a většina síťového I/O jsou skutečně neblokující a řídí je systémový poller (epoll/kqueue/IOCP). Operace se souborovým systémem (část modulu fs) se provádějí ve fondu vláken knihovny libuv (výchozí velikost bývá 4, nastavit ji lze přes UV_THREADPOOL_SIZE). Intenzivní práce s diskem proto může obsadit dostupná vlákna a zvýšit latenci – sledujte fronty a případně zvětšete fond vláken nebo použijte streaming a dávkové zpracování.
Streams a zpětný tlak (backpressure)
stream.Readable/Writable poskytují proudové API s řízením rychlosti. Důležité je respektovat návratovou hodnotu metody write() a počkat na událost 'drain'. Tím zabráníte přeplnění bufferů a stabilizujete latenci. Metoda pipe (readable.pipe(writable)) řeší zpětný tlak automaticky.
Programovací model: callbacky, Promise a async/await
- Callbacky (historicky kontrakt error-first
(err, data)) – stále se používají v některých API (např.fs). - Promise a async/await: moderní API (
fs/promises,timers/promises) s přehledným řízením chyb pomocítry/catch. Pozor na paralelizaci:awaitv cyklu operace serializuje; použijtePromise.all/allSettled.
Garbage collector a výkon
V8 používá generační GC. Dlouhé pauzy snižují propustnost. Doporučená opatření: vyhýbejte se masivním alokacím v horkých smyčkách, recyklujte objekty tam, kde to dává smysl, při I/O upřednostňujte sdružování bufferů Buffer, měřte využití haldy pomocí nástrojů inspector a heap snapshots. U služeb citlivých na latenci je vhodné sledovat zpoždění event loopu a statistiky GC.
Moduly: CommonJS vs. ECMAScript Modules
Node podporuje CJS (require, module.exports) i ESM (import/export). Režim určují soubor package.json ("type": "module") a přípony souborů (.mjs/.cjs). Kombinování formátů vyžaduje zohlednit specifická pravidla (např. createRequire), proto v projektu upřednostňujte jednotný styl a explicitní mapy exportů (exports).
Worker Threads a Cluster
- Worker Threads: skutečně paralelně zpracovávají úlohy náročné na CPU v rámci jednoho procesu, s izolovanými haldami, sdílenou pamětí (SharedArrayBuffer) a komunikací předáváním zpráv (message-passing). Používejte je pro kompresi, šifrování a 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í haldu a event loop.
Nativní doplňky (Addons) a N-API
Pro kritické úseky kódu lze psát nativní moduly v C/C++ pomocí N-API, které stabilizuje ABI napříč verzemi Node.js. Hodí se pro obálky knihoven operačního systému, kryptografii, parsování nebo náročné výpočty. Dbejte na přenositelnost, správu paměti a bezpečnost při práci s vlákny.
HTTP server a síťové vzory
Server http/http2 je založený na událostech: každé spojení představuje socket, který zpracovává požadavky a odpovědi jako streamy. Optimalizace: keep-alive, opakované využití spojení, kompaktní hlavičky, podpora HTTP/2, komprese (zlib/brotli), správné nastavení cache-control a ETag. Zvažte ochranu pomocí WAF, omezení počtu požadavků, časové limity (headersTimeout, requestTimeout), omezení velikosti body a ochranu proti útokům typu slow-loris.
Bezpečnost: běhové prostředí i závislosti
- Kontrolujte závislosti pomocí bezpečnostního auditu, zamykejte verze (
lockfile) a minimalizujte útočnou plochu. - Chraňte se proti zneužití prototypů (Prototype Pollution), SSRF, ReDOS (vyhýbejte se patologickým regulárním výrazům), XSS v šablonách a path traversal.
- V OpenSSL používejte bezpečné křivky a šifry, při odchozích spojeních ověřujte certifikáty a názvy hostitelů.
Observabilita: metriky, logy a tracing
Měřte zpoždění event loopu, počet otevřených handlerů, využití haldy a CPU, latenci endpointů a chybovost. Pro tracing použijte OpenTelemetry a W3C Trace Context. Zaznamenávejte strukturované logy (JSON) s korelačními ID a dbejte na ochranu osobních údajů.
Diagnostika a profilování
- Inspector (
--inspect, DevTools) pro krokování a profilování CPU a haldy. - perf/0x/clinic pro plamenné grafy a hledání výkonnostních úzkých míst.
- Async Hooks pro sledování životního cyklu asynchronních zdrojů (používejte opatrně – mohou snížit výkon).
Správa procesu: signály, fronty a vypínání
Implementujte řízené vypnutí: zachytávejte SIGTERM, přestaňte přijímat nová spojení, počkejte na dokončení rozpracovaných požadavků, uvolněte zdroje a ukončete proces s kódem 0. V kontejnerovém prostředí zohledněte sondy liveness/readiness a zpětný tlak z ingress vrstvy.
Zásady výkonu a anti-patterny
- Neprovádějte smyčky náročné na CPU v hlavním vlákně – použijte Worker Threads nebo přesuňte zátěž na jiné služby (offload).
- Vyhýbejte se synchronním API (
fs.readFileSync) při zpracování požadavků. - Upřednostňujte streamy před načítáním celého obsahu do paměti.
- Zpracovávejte I/O dávkově a slučujte je (coalesce), využívejte sdružování spojení.
- Měřte, profilujte a iterujte – odhad není metrika.
Správa balíčků a nasazení
package.json definuje skripty (start, build), mapy exportů, typ modulu, engines a bin. Pro rychlejší start používejte prebuild, tree-shaking a minimalizujte kroky transpile za běhu. V produkci uzamkněte lockfile, nastavte NODE_ENV=production, hlídejte velikost image a nastavte limity paměti.
Kompatibilita a verze Node.js
Pro produkční prostředí zvolte řadu LTS. Sledujte zásadní změny podle semver (např. výchozí chování ESM, nová API v fetch, stabilizaci test runner). Testujte s více verzemi pomocí CI matice a v engines definujte minimální podporované verze.
Typické architektonické vzory
- API Gateway/Backend-for-Frontend: agregace dat, cache, omezení počtu požadavků, 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 výkonnou standardní knihovnou. Pochopení fází smyčky, rozdílu mezi mikroúlohami a makroúlohami, role fondu vláken 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 úlohy náročné na CPU a pečlivým vypínáním – umožňuje škálovat od jednoduchých API až po globální realtime platformy.
