Node.js
Node.js

Sledovat

24. února 2017 – 14 minut čtení

Tento článek pochází od Tomislava Capana, technického konzultanta a nadšence do Node.js. Tomislav jej původně publikoval v srpnu 2013 na blogu společnosti Toptal – původní příspěvek najdete zde; blog byl mírně aktualizován. Následující téma vychází z názorů a zkušeností tohoto autora.

Rostoucí popularita JavaScriptu s sebou přinesla mnoho změn a tvář dnešního vývoje webových stránek je dramaticky odlišná. Věci, které dnes můžeme na webu dělat pomocí JavaScriptu běžícího na serveru i v prohlížeči, byly ještě před několika lety těžko představitelné nebo byly zapouzdřené v sandboxovaných prostředích, jako je Flash nebo Java Applets.

Než se začtete do Node.js, možná byste si měli přečíst o výhodách používání JavaScriptu napříč celým stackem, který sjednocuje jazyk a formát dat (JSON), což umožňuje optimálně znovu využívat vývojářské prostředky. Protože se jedná spíše o výhodu jazyka JavaScript než konkrétně Node.js, nebudeme se jí zde příliš zabývat. Je to však klíčová výhoda začlenění Node.js do vašeho zásobníku.

Node.js je běhové prostředí JavaScriptu postavené na JavaScriptovém enginu V8 Chrome. Stojí za zmínku, že cílem Ryana Dahla, tvůrce Node.js, bylo vytvořit webové stránky v reálném čase s funkcí push, „inspirované aplikacemi jako Gmail“. V Node.js dal vývojářům nástroj pro práci v neblokujícím, událostmi řízeném I/O paradigmatu.

Jednou větou: Node.js září ve webových aplikacích v reálném čase využívajících technologii push přes webové sockety. Co je na tom tak revolučního? Inu, po více než 20 letech bezstavového webu založeného na bezstavovém paradigmatu požadavek-odpověď máme konečně webové aplikace s obousměrnými spojeními v reálném čase, kdy klient i server mohou iniciovat komunikaci, což jim umožňuje volnou výměnu dat.

To je v ostrém kontrastu s typickým paradigmatem webové odpovědi, kdy komunikaci vždy iniciuje klient. Navíc je vše založeno na otevřeném webovém zásobníku (HTML, CSS a JS), který běží přes standardní port 80.

Někdo by mohl namítnout, že toto máme již léta v podobě Flash a Java Appletů – ale ve skutečnosti to byla jen sandboxovaná prostředí využívající web jako transportní protokol, který má být doručen klientovi. Navíc byly provozovány izolovaně a často fungovaly na nestandardních portech, což mohlo vyžadovat další oprávnění a podobně.

Se všemi svými výhodami hraje nyní Node.js klíčovou roli v technologickém zásobníku mnoha významných společností, které jsou na jeho jedinečných výhodách závislé. Node.js Foundation shrnula všechny nejlepší úvahy o tom, proč by podniky měly uvažovat o Node.js, do krátké prezentace, kterou najdete na stránce Node.js Foundation’s Case Studies.

V tomto příspěvku se budu zabývat nejen tím, jak je těchto výhod dosaženo, ale také tím, proč byste mohli chtít Node.js používat – a proč ne – na příkladu některých klasických modelů webových aplikací.

Jak to funguje?“

Hlavní myšlenka Node.js: používat neblokující, událostmi řízený vstup/výstup, abyste zůstali lehcí a efektivní tváří v tvář datově náročným aplikacím v reálném čase, které běží na distribuovaných zařízeních.

To je plná huba.

Ve skutečnosti to znamená, že Node.js není nová platforma se stříbrnou kuličkou, která ovládne svět webového vývoje. Místo toho je to platforma, která naplňuje konkrétní potřebu. A pochopení této skutečnosti je naprosto zásadní. Rozhodně nechcete používat Node.js pro operace náročné na procesor; ve skutečnosti jeho použití pro náročné výpočty zruší téměř všechny jeho výhody. Kde Node.js skutečně září, je vytváření rychlých a škálovatelných síťových aplikací, protože je schopen zpracovávat obrovské množství současných připojení s vysokou propustností, což se rovná vysoké škálovatelnosti.

Jak funguje pod kapotou, je docela zajímavé. V porovnání s tradičními technikami webové obsluhy, kdy každé spojení (požadavek) vyvolá nové vlákno, které zabírá systémovou paměť RAM a nakonec vyčerpá maximum dostupné paměti RAM, Node.js pracuje v jednom vlákně a používá neblokující I/O volání, což mu umožňuje podporovat desítky tisíc souběžných připojení (držených ve smyčce událostí).

*Obrázek převzat z původního příspěvku na blogu.

Rychlý výpočet: za předpokladu, že každé vlákno má s sebou potenciálně 2 MB doprovodné paměti, při běhu na systému s 8 GB RAM se dostáváme na teoretické maximum 4000 souběžných spojení (výpočty převzaty z článku Michaela Abernethyho „Just what is Node.js?“, publikovaného na IBM developerWorks v roce 2011; článek již bohužel není k dispozici), plus náklady na přepínání kontextu mezi vlákny. To je scénář, který obvykle řešíte v tradičních technikách webové obsluhy. Tím, že se tomu všemu Node.js vyhýbá, dosahuje úrovně škálovatelnosti přes 1 milion souběžných spojení a přes 600 tisíc souběžných spojení websocketů.

Je tu samozřejmě otázka sdílení jednoho vlákna mezi všemi požadavky klientů a to je potenciální úskalí psaní aplikací Node.js. Zaprvé, náročné výpočty by mohly zadusit jediné vlákno Node a způsobit problémy všem klientům (více o tom později), protože příchozí požadavky by byly blokovány, dokud by zmíněné výpočty nebyly dokončeny. Za druhé, vývojáři musí být opravdu opatrní, aby nedopustili, že výjimka probublá až do jádra (nejvyšší smyčky událostí) Node.js, což způsobí ukončení instance Node.js (efektivní pád programu).

Technika používaná k zamezení probublávání výjimek na povrch je předávání chyb zpět volajícímu jako parametry zpětného volání (místo jejich zahazování, jako v jiných prostředích). I kdyby se nějaké neošetřené výjimce podařilo probublat na povrch, byly vyvinuty nástroje, které monitorují proces Node.js a provádějí potřebné obnovení havarované instance (i když se vám pravděpodobně nepodaří obnovit aktuální stav uživatelské relace), nejčastěji je to modul Forever nebo použití jiného přístupu s externími systémovými nástroji upstart a monit, nebo dokonce jen upstart.

npm: Správce balíčků Node

Při diskusi o Node.js by rozhodně neměla být opomenuta vestavěná podpora správy balíčků pomocí nástroje npm, který je standardně dodáván s každou instalací Node.js. Myšlenka modulů npm je dosti podobná myšlence Ruby Gems: sada veřejně dostupných, opakovaně použitelných komponent, které jsou k dispozici prostřednictvím snadné instalace přes online repozitář, se správou verzí a závislostí.

Úplný seznam balíčkových modulů najdete na webových stránkách npm nebo k nim získáte přístup pomocí nástroje npm CLI, který se automaticky instaluje s Node.js. Ekosystém modulů je otevřený všem a každý může publikovat svůj vlastní modul, který bude uveden v úložišti npm. Stručný úvod do systému npm najdete v příručce pro začátečníky a podrobnosti o publikování modulů v příručce npm Publishing Tutorial.

Mezi nejužitečnější moduly npm dnes patří:

  • express – Express.js, framework pro vývoj webových aplikací inspirovaný Sinatrou pro Node.js a de facto standard pro většinu dnešních aplikací pro Node.js.
  • hapi – velmi modulární a snadno použitelný konfigurační framework pro vytváření webových aplikací a aplikací pro služby
  • connect – Connect je rozšiřitelný framework pro HTTP server Node.js, který poskytuje kolekci vysoce výkonných „zásuvných modulů“ známých jako middleware; slouží jako základní stavební kámen pro Express.
  • socket.io a sockjs – Komponenta na straně serveru dvou dnes nejrozšířenějších komponent pro webové sockety.
  • pug (dříve Jade) – Jeden z populárních šablonovacích enginů, inspirovaný HAML, výchozí v Express.js.
  • mongodb a mongojs – Obaly MongoDB poskytující API pro objektové databáze MongoDB v Node.js.
  • redis – Klientská knihovna Redis.
  • lodash (underscore, lazy.js) – Užitkový pás JavaScriptu. Underscore zahájil hru, ale byl převálcován jedním ze svých dvou protějšků, především díky lepšímu výkonu a modulární implementaci.
  • forever – Pravděpodobně nejrozšířenější utilita pro zajištění nepřetržitého běhu daného uzlového skriptu. Udrží váš proces Node.js v produkčním režimu v chodu i při neočekávaných výpadcích.
  • bluebird – Plnohodnotná implementace Promises/A+ s mimořádně dobrým výkonem
  • moment – Odlehčená knihovna JavaScript pro analýzu, validaci, manipulaci a formátování dat.

Seznam by mohl pokračovat. Existují tuny opravdu užitečných balíčků, které jsou dostupné všem (bez urážky těch, které jsem zde vynechal).

Kde by se měl Node.js používat

Chat je nejtypičtější aplikací v reálném čase pro více uživatelů. Od IRC (kdysi), přes mnoho proprietárních i otevřených protokolů běžících na nestandardních portech, až po možnost implementovat vše dnes v Node.js s websockety běžícími přes standardní port 80.

Aplikace chatu je opravdu sladkým příkladem pro Node.js: je to lehká aplikace s velkým provozem, náročná na data (ale s nízkou náročností na zpracování/výpočet), která běží na distribuovaných zařízeních. Je to také skvělý případ použití i pro učení, protože je jednoduchý, a přitom pokrývá většinu paradigmat, která kdy použijete v typické aplikaci Node.js.

Zkusme si znázornit, jak to funguje.

V nejjednodušším scénáři máme na našem webu jednu chatovací místnost, kam chodí lidé a mohou si vyměňovat zprávy způsobem one-to-many (vlastně všichni). Řekněme, že máme na webu tři lidi, kteří jsou všichni připojeni k našemu diskusnímu fóru.

Na straně serveru máme jednoduchou aplikaci Express.js, která implementuje dvě věci: 1) obsluhu požadavku GET ‚/‘, která obsluhuje webovou stránku obsahující nástěnku a tlačítko ‚Odeslat‘ pro inicializaci vstupu nové zprávy, a 2) websocketový server, který naslouchá novým zprávám vysílaným websocketovými klienty.

Na straně klienta máme stránku HTML s několika nastavenými obsluhami, jednou pro událost kliknutí na tlačítko ‚Odeslat‘, která zachytí vstupní zprávu a odešle ji po websocketu, a druhou, která naslouchá novým příchozím zprávám na websocketovém klientu (tj, zprávy odeslané jinými uživateli, které nyní server chce, aby klient zobrazil).

Když jeden z klientů odešle zprávu, stane se toto:

  • Prohlížeč zachytí kliknutí na tlačítko ‚Odeslat‘ prostřednictvím obslužného programu JavaScript, vyzvedne hodnotu ze vstupního pole (tj, text zprávy) a vyšle zprávu websocket pomocí klienta websocket připojeného k našemu serveru (inicializovaného při inicializaci webové stránky).
  • Komponenta na straně serveru připojení websocket přijme zprávu a předá ji všem ostatním připojeným klientům pomocí metody broadcast.
  • Všichni klienti obdrží novou zprávu jako push zprávu prostřednictvím komponenty websocket na straně klienta spuštěné v rámci webové stránky. Poté převezmou obsah zprávy a aktualizují webovou stránku na místě připojením nové zprávy na nástěnku.

Obrázek převzat z původního blogu.

Toto je nejjednodušší příklad. Pro robustnější řešení můžete použít jednoduchou mezipaměť založenou na úložišti Redis. Nebo v ještě pokročilejším řešení frontu zpráv, která se stará o směrování zpráv klientům, a robustnější mechanismus doručování, který může pokrýt dočasné ztráty spojení nebo ukládání zpráv pro registrované klienty v době, kdy jsou offline. Bez ohledu na provedená vylepšení však bude Node.js stále pracovat na stejných základních principech: reagovat na události, zvládat mnoho souběžných připojení a zachovávat plynulost uživatelského prostředí.

API NAD OBJEKTOVOU DB

Přestože Node.js skutečně září v aplikacích reálného času, zcela přirozeně se hodí pro vystavení dat z objektových DB (např. MongoDB). Data uložená v JSON umožňují Node.js fungovat bez impedančního nesouladu a konverze dat.

Příklad pokud používáte Rails, převedete je z JSON na binární modely a pak je vystavíte zpět jako JSON přes HTTP, když jsou data konzumována React.js, Angular.js atd. nebo dokonce obyčejným voláním jQuery AJAX. V Node.js můžete jednoduše vystavit své objekty JSON pomocí rozhraní REST API, aby je klient mohl konzumovat. Navíc se nemusíte starat o konverzi mezi JSON a čímkoli jiným při čtení nebo zápisu z databáze (pokud používáte MongoDB). Celkově se můžete vyhnout nutnosti vícenásobných konverzí tím, že použijete jednotný formát serializace dat napříč klientem, serverem a databází.

KVALITNÍ VSTUPY

Pokud přijímáte velké množství dat současně, může se vaše databáze stát úzkým hrdlem. Jak je znázorněno výše, Node.js si se souběžnými připojeními snadno poradí sám. Ale protože přístup k databázi je (v tomto případě) blokující operace, narážíme na potíže. Řešením je potvrzení chování klienta před skutečným zápisem dat do databáze.

S tímto přístupem si systém zachovává odezvu i při velkém zatížení, což je užitečné zejména tehdy, když klient nepotřebuje pevné potvrzení o úspěšném zápisu dat. Typickými příklady jsou: záznam nebo zápis údajů o sledování uživatelů, které se zpracovávají v dávkách a používají se až později, a také operace, které se nemusí projevit okamžitě (například aktualizace počtu „Líbí se mi“ na Facebooku), kde je přijatelná případná konzistence (tak často používaná ve světě NoSQL).

Data se řadí do fronty prostřednictvím nějakého druhu cache nebo infrastruktury pro řazení zpráv (MQ) (např, RabbitMQ, ZeroMQ) a zpracovávány samostatným procesem dávkového zápisu do databáze nebo backendovými službami náročnými na zpracování výpočtů, napsanými na výkonnější platformě pro takové úlohy. Podobné chování lze implementovat i v jiných jazycích/frameworcích, ale ne na stejném hardwaru, se stejně vysokou a zachovanou propustností.

Obrázek převzat z původního článku.

Zkrátka: pomocí Node můžete zápisy do databáze odsunout stranou a zabývat se jimi později a postupovat, jako by se to podařilo.

DATA STREAMING

V tradičnějších webových platformách se s požadavky a odpověďmi HTTP zachází jako s izolovanými událostmi; ve skutečnosti jsou to vlastně proudy. Toto pozorování lze využít v systému Node.js a vytvořit několik skvělých funkcí. Například je možné zpracovávat soubory ještě během jejich nahrávání, protože data přicházejí prostřednictvím streamu a my je můžeme zpracovávat online. To lze provést pro kódování zvuku nebo videa v reálném čase a proxy mezi různými zdroji dat (viz další část).

PROXY

Node.js lze snadno použít jako proxy na straně serveru, kde může zpracovávat velké množství současných připojení neblokujícím způsobem. Je obzvláště užitečný pro proxy různých služeb s různou dobou odezvy nebo pro sběr dat z více zdrojových bodů.

Příklad: Uvažujte o aplikaci na straně serveru, která komunikuje se zdroji třetích stran, stahuje data z různých zdrojů nebo ukládá prostředky, jako jsou obrázky a videa, do cloudových služeb třetích stran.

Ačkoli existují specializované proxy servery, použití Node místo nich může být užitečné, pokud vaše proxy infrastruktura neexistuje nebo pokud potřebujete řešení pro místní vývoj. Tím mám na mysli, že byste mohli vytvořit aplikaci na straně klienta s vývojovým serverem Node.js pro aktiva a proxying/stubbing požadavků API, zatímco v produkci byste tyto interakce řešili pomocí dedikované proxy služby (nginx, HAProxy atd.).

BROKERAGE – STOCK TRADER’S DASHBOARD

Přejděme zpět na úroveň aplikace. Dalším příkladem, kde převažuje desktopový software, který by však mohl být snadno nahrazen webovým řešením v reálném čase, je obchodní software makléřů, který se používá ke sledování cen akcií, provádění výpočtů/technické analýzy a vytváření grafů/nákresů.

Přechod na webové řešení v reálném čase by makléřům umožnil snadno měnit pracovní stanice nebo pracovní místa. Brzy bychom je mohli začít vídat na pláži na Floridě… nebo na Ibize… nebo na Bali.

APLIKACE MONITOROVACÍHO PANELU

Další běžný případ použití, do kterého se uzel s webovými zásuvkami dokonale hodí: sledování návštěvníků webových stránek a vizualizace jejich interakcí v reálném čase. Můžete shromažďovat statistiky o uživatelích v reálném čase, nebo je dokonce posunout na další úroveň zavedením cílených interakcí s návštěvníky otevřením komunikačního kanálu, když dosáhnou určitého bodu ve vašem trychtýři – příkladem může být CANDDi.

Představte si, jak byste mohli zlepšit své podnikání, kdybyste věděli, co vaši návštěvníci dělají v reálném čase – kdybyste mohli vizualizovat jejich interakce. Díky obousměrným soketům v reálném čase v systému Node.js to nyní můžete.

MONITOROVACÍ TABULKA SYSTÉMU

Nyní navštívíme infrastrukturní stránku věci. Představte si například poskytovatele SaaS, který chce svým uživatelům nabídnout stránku pro monitorování služeb (například stránku GitHub Status). Pomocí smyčky událostí Node.js můžeme vytvořit výkonný webový řídicí panel, který asynchronním způsobem kontroluje stavy služeb a odesílá data klientům pomocí websocketů.

Pomocí této technologie lze živě a v reálném čase hlásit stavy interních (vnitrofiremních) i veřejných služeb. Posuňte tuto myšlenku ještě o kousek dál a zkuste si představit síťové operační centrum (NOC) monitorující aplikace u telekomunikačního operátora, poskytovatele cloudu/sítě/hostingu nebo nějaké finanční instituce, které by běžely na otevřeném webovém zásobníku podpořeném Node.js a websockety namísto Javy a/nebo javových apletů.

Poznámka: Nesnažte se v Node.js budovat systémy pracující v tvrdém reálném čase (tj. systémy vyžadující konzistentní dobu odezvy). Pro tuto třídu aplikací je pravděpodobně lepší volbou Erlang.

WEBOVÉ APLIKACE NA STRANĚ SERVERU

Node.js s Express.js lze použít také k vytváření klasických webových aplikací na straně serveru. Nicméně, i když je to možné, toto paradigma požadavek-odpověď, ve kterém by Node.js přenášel vykreslené HTML, není nejtypičtějším případem použití. Existují argumenty pro i proti tomuto přístupu. Zde je několik faktů, které je třeba zvážit:

Pros:

  • Pokud vaše aplikace neobsahuje žádné výpočty náročné na procesor, můžete ji sestavit v Javascriptu odshora dolů, dokonce až na úrovni databáze, pokud používáte JSON storage Object DB jako MongoDB. To výrazně usnadňuje vývoj (včetně najímání).
  • Prohlížeči dostanou plně vykreslenou odpověď HTML, která je mnohem přívětivější pro SEO než například aplikace typu Single Page Application nebo websockets spuštěná nad Node.js.

Nevýhody:

  • Jakékoli výpočty náročné na CPU zablokují odezvu Node.js, proto je lepší použít vláknovou platformu. Případně můžete zkusit výpočet škálovat(*).
  • Použití Node.js s relační databází je stále docela oříšek (podrobněji viz níže). Udělejte si laskavost a vyberte si nějaké jiné prostředí, jako je Rails, Django nebo ASP.Net MVC, pokud se snažíte provádět relační operace.

(*) Alternativou k výpočtům náročným na procesor je vytvoření vysoce škálovatelného prostředí s podporou MQ s back-endovým zpracováním, aby Node zůstal jako front-facingový „úředník“ pro asynchronní zpracování klientských požadavků.

SERVER-SIDE WEBOVÁ APLIKACE S RELAČNÍ DATABÁZÍ V ZÁLOZE

Při porovnání Node.js s Express.js například proti Ruby on Rails je jednoznačně rozhodnuto ve prospěch druhého jmenovaného, pokud jde o relační přístup k datům.

Přístroje pro relační DB pro Node.js jsou ve srovnání s konkurencí stále poměrně málo rozvinuté. Na druhou stranu Rails poskytuje automatické nastavení přístupu k datům hned po vybalení z krabice spolu s nástroji pro podporu migrace DB schémat a dalšími skvosty (slovní hříčka). Rails a jemu podobné frameworky mají vyzrálé a osvědčené implementace vrstvy přístupu k datům Active Record nebo Data Mapper, které vám budou velmi chybět, pokud se je pokusíte replikovat v čistém JavaScriptu (*)

Přesto, pokud jste opravdu nakloněni zůstat JS se vším všudy, podívejte se na Sequelize a Node ORM2.

(*) Je možné, a ne neobvyklé, používat Node.js pouze jako fasádu pro veřejnost a přitom si ponechat back-end Rails a jeho snadný přístup k relační DB.

Těžké výpočty/procesování na straně serveru

Pokud jde o těžké výpočty, Node.js není tou nejlepší platformou. Ne, v Node.js rozhodně nechcete stavět server pro Fibonacciho výpočty. Obecně platí, že jakákoli operace náročná na procesor anuluje všechny výhody propustnosti, které Node nabízí díky svému událostmi řízenému neblokujícímu modelu I/O, protože všechny příchozí požadavky budou blokovány, zatímco vlákno bude zaměstnáno vaším počítáním čísel.

Jak již bylo uvedeno, Node.js je jednovláknový a využívá pouze jedno jádro procesoru. Pokud jde o přidání souběhu na vícejádrovém serveru, tým jádra Node na tom pracuje v podobě clusterového modulu. Můžete také docela snadno spustit několik instancí serveru Node.js za reverzním proxy serverem přes nginx.

Při použití clusteru byste měli ještě všechny těžké výpočty přenést na procesy na pozadí napsané v prostředí, které je pro to vhodnější, a nechat je komunikovat prostřednictvím serveru s frontami zpráv, jako je RabbitMQ.

Přestože zpracování na pozadí může zpočátku probíhat na stejném serveru, má takový přístup potenciál velmi vysoké škálovatelnosti. Tyto služby zpracování na pozadí by bylo možné snadno rozdělit na samostatné pracovní servery, aniž by bylo nutné konfigurovat zátěž webových serverů na frontě.

Stejný přístup byste samozřejmě použili i na jiných platformách, ale s Node.js získáte vysokou propustnost reqs/sec, o které jsme mluvili, protože každý požadavek je malá úloha zpracovaná velmi rychle a efektivně.

Závěr

Probrali jsme Node.js od teorie k praxi, počínaje jeho cíli a ambicemi a konče jeho sladkými místy a úskalími. Když lidé narazí na problémy s Node, téměř vždy se to svede na to, že blokační operace jsou kořenem všeho zla – 99 % nesprávných použití Node je jejich přímým důsledkem.

Zapomeňte: Node.js nikdy nevznikl proto, aby řešil problém škálování výpočetního výkonu. Byl vytvořen k řešení problému škálování I/O, což se mu daří opravdu dobře.