De ce naiba aș folosi Node.js? Un tutorial de la caz la caz
Publicat: 2022-03-11Introducere
Popularitatea în creștere a JavaScript a adus cu ea o mulțime de schimbări, iar fața dezvoltării web de astăzi este dramatic diferită. Lucrurile pe care le putem face pe web în prezent, cu JavaScript rulând pe server, precum și în browser, erau greu de imaginat cu doar câțiva ani în urmă sau au fost încapsulate în medii sandbox precum Flash sau Java Applets.
Înainte de a explora soluțiile Node.js, poate doriți să citiți despre beneficiile utilizării JavaScript în stiva, care unifică limbajul și formatul de date (JSON), permițându-vă să reutilizați în mod optim resursele dezvoltatorului. Deoarece acesta este mai mult un beneficiu al JavaScript-ului decât Node.js în mod specific, nu vom discuta prea mult aici. Dar este un avantaj cheie pentru a include Node în stiva dumneavoastră.
După cum afirmă Wikipedia: „Node.js este o compilație a motorului JavaScript V8 al Google, stratul de abstractizare a platformei libuv și o bibliotecă de bază, care este ea însăși scrisă în principal în JavaScript.” Dincolo de asta, merită remarcat faptul că Ryan Dahl, creatorul Node.js, urmărea să creeze site-uri web în timp real cu capacitate push , „inspirate de aplicații precum Gmail”. În Node.js, le-a oferit dezvoltatorilor un instrument pentru a lucra în paradigma I/O neblocante, bazată pe evenimente.
Într-o singură propoziție: Node.js strălucește în aplicațiile web în timp real care folosesc tehnologia push peste websocket-uri. Ce este atât de revoluționar în asta? Ei bine, după peste 20 de ani de apatrid-web bazat pe paradigma apatride cerere-răspuns, avem în sfârșit aplicații web cu conexiuni bidirecționale în timp real, unde atât clientul, cât și serverul pot iniția comunicarea, permițându-le să schimbe date liber. . Acest lucru este în contrast puternic cu paradigma tipică de răspuns web, în care clientul inițiază întotdeauna comunicarea. În plus, totul se bazează pe stiva web deschisă (HTML, CSS și JS) care rulează pe portul standard 80.
S-ar putea argumenta că am avut acest lucru de ani de zile sub formă de Applet-uri Flash și Java, dar, în realitate, acestea erau doar medii sandbox care foloseau web-ul ca protocol de transport pentru a fi livrat clientului. În plus, acestea au fost rulate izolat și adesea operate prin porturi non-standard, care ar fi putut necesita permisiuni suplimentare și altele.
Cu toate avantajele sale, Node.js joacă acum un rol critic în tehnologia multor companii de profil, care depind de beneficiile sale unice. Fundația Node.js a consolidat toate cele mai bune gândiri despre motivul pentru care întreprinderile ar trebui să ia în considerare Node.js într-o scurtă prezentare care poate fi găsită pe pagina Studii de caz a Fundației Node.js.
În acest ghid Node.js, voi discuta nu numai cum sunt realizate aceste avantaje, ci și de ce ați putea dori să utilizați Node.js – și de ce nu – folosind unele dintre modelele clasice de aplicații web ca exemple.
Cum functioneazã?
Ideea principală a Node.js: folosiți I/O neblocante, bazate pe evenimente, pentru a rămâne ușoare și eficiente în fața aplicațiilor în timp real care consumă intens date, care rulează pe dispozitive distribuite.
Asta e o gură.
Ceea ce înseamnă cu adevărat este că Node.js nu este o nouă platformă de argint care va domina lumea dezvoltării web. În schimb, este o platformă care satisface o anumită nevoie . Și înțelegerea acestui lucru este absolut esențială. Cu siguranță nu doriți să utilizați Node.js pentru operațiuni care necesită mult CPU; de fapt, folosirea lui pentru calcule grele va anula aproape toate avantajele sale. Acolo unde Node strălucește cu adevărat este în construirea de aplicații de rețea rapide și scalabile, deoarece este capabil să gestioneze un număr mare de conexiuni simultane cu un randament ridicat, ceea ce echivalează cu o scalabilitate ridicată.
Cum funcționează sub capotă este destul de interesant. În comparație cu tehnicile tradiționale de servire web, în care fiecare conexiune (cerere) generează un nou fir de execuție, ocupând memoria RAM de sistem și, în cele din urmă, maximizând cantitatea de RAM disponibilă, Node.js funcționează pe un singur fir, folosind I/ non-blocante. Apeluri O, permițându-i să accepte zeci de mii de conexiuni simultane menținute în bucla de eveniment.
Un calcul rapid: presupunând că fiecare fir are potențial 2 MB de memorie însoțitoare, rularea pe un sistem cu 8 GB RAM ne pune la un maxim teoretic de 4.000 de conexiuni concurente (calculele luate din articolul lui Michael Abernethy „Ce este Nodul .js?”, publicat pe IBM developerWorks în 2011; din păcate, articolul nu mai este disponibil) , plus costul comutării contextului între fire. Acesta este scenariul cu care vă ocupați de obicei în tehnicile tradiționale de servire web. Evitând toate acestea, Node.js atinge niveluri de scalabilitate de peste 1 milion de conexiuni simultane și peste 600.000 de conexiuni websocket-uri simultane.
Există, desigur, problema partajării unui singur fir între toate solicitările clienților și este o potențială capcană a scrierii aplicațiilor Node.js. În primul rând, calculele grele ar putea sufoca firul de execuție al lui Node și ar putea cauza probleme tuturor clienților (mai multe despre asta mai târziu), deoarece cererile primite ar fi blocate până la finalizarea calculului menționat. În al doilea rând, dezvoltatorii trebuie să fie foarte atenți să nu permită ca o excepție să apară până la bucla de evenimente de bază (cel mai de sus) Node.js, ceea ce va duce la terminarea instanței Node.js (în mod efectiv blocarea programului).
Tehnica folosită pentru a evita excepțiile să apară la suprafață este transmiterea erorilor înapoi apelantului ca parametri de apel invers (în loc să le arunce, ca în alte medii). Chiar dacă o excepție netratată reușește să apară, au fost dezvoltate instrumente pentru a monitoriza procesul Node.js și pentru a efectua recuperarea necesară a unei instanțe accidentate (deși probabil că nu veți putea recupera starea curentă a sesiunii utilizator), cel mai obișnuit fiind modulul Forever sau utilizarea unei abordări diferite cu instrumente de sistem externe upstart și monit , sau chiar doar parvenit.
NPM: Managerul de pachete Node
Când discutăm despre Node.js, un lucru care cu siguranță nu ar trebui omis este suportul încorporat pentru gestionarea pachetelor folosind NPM, un instrument care vine în mod implicit la fiecare instalare Node.js. Ideea modulelor NPM este destul de similară cu cea a Ruby Gems : un set de componente disponibile public, reutilizabile, disponibile prin instalare ușoară printr-un depozit online, cu management al versiunilor și al dependenței.
O listă completă a modulelor împachetate poate fi găsită pe site-ul web npm sau accesată folosind instrumentul CLI npm care se instalează automat cu Node.js. Ecosistemul de module este deschis tuturor și oricine își poate publica propriul modul care va fi listat în depozitul npm.
Unele dintre cele mai utile module npm astăzi sunt:
- express - Express.js—sau pur și simplu Express—un cadru de dezvoltare web inspirat de Sinatra pentru Node.js și standardul de facto pentru majoritatea aplicațiilor Node.js existente astăzi.
- hapi - un cadru foarte modular și simplu de utilizat, centrat pe configurație pentru construirea de aplicații web și servicii
- connect - Connect este un cadru de server HTTP extensibil pentru Node.js, care oferă o colecție de „plugin-uri” de înaltă performanță cunoscute sub numele de middleware; servește ca bază de bază pentru Express.
- socket.io și sockjs - componentă de pe partea serverului a celor mai comune două componente websockets existente astăzi.
- pug (fost Jade ) - Unul dintre motoarele de șabloane populare, inspirat de HAML, implicit în Express.js.
- mongodb și mongojs - Wrapper-uri MongoDB pentru a furniza API-ul pentru bazele de date de obiecte MongoDB în Node.js.
- redis - Biblioteca client Redis.
- lodash (subliniere, lazy.js) - centura de utilitate JavaScript. Underscore a inițiat jocul, dar a fost răsturnat de unul dintre cei doi omologi ai săi, în principal datorită performanței mai bune și implementării modulare.
- pentru totdeauna - Probabil cea mai comună utilitate pentru a se asigura că un anumit script nod rulează continuu. Menține procesul Node.js în producție în fața oricăror eșecuri neașteptate.
- bluebird - O implementare completă Promises/A+ cu performanțe excepțional de bune
- moment - O bibliotecă de date JavaScript pentru analizarea, validarea, manipularea și formatarea datelor.
Lista continuă. Există o mulțime de pachete cu adevărat utile, disponibile pentru toți (fără supărare pentru cei pe care i-am omis aici).
Exemple de unde ar trebui folosit Node.js
CONVERSAȚIE
Chat este cea mai tipică aplicație în timp real, multi-utilizator. De la IRC (pe vremuri), prin multe protocoale proprietare și deschise care rulează pe porturi non-standard, până la capacitatea de a implementa totul astăzi în Node.js cu websocket-uri care rulează pe portul standard 80.
Aplicația de chat este într-adevăr exemplul favorabil pentru Node.js: este o aplicație ușoară, cu trafic ridicat, cu consum mare de date (dar cu procesare/calcul redus), care rulează pe dispozitive distribuite. Este, de asemenea, un caz de utilizare grozav și pentru învățare, deoarece este simplu, dar acoperă majoritatea paradigmelor pe care le veți folosi vreodată într-o aplicație tipică Node.js.
Să încercăm să descriem cum funcționează.
În cel mai simplu exemplu, avem o singură cameră de chat pe site-ul nostru web, unde oamenii vin și pot schimba mesaje într-un mod unul la mai mulți (de fapt, toate). De exemplu, să presupunem că avem trei persoane pe site, toate conectate la panoul de mesaje.
Pe partea de server, avem o aplicație simplă Express.js care implementează două lucruri:
- Un handler
GET /
cerere care servește pagina web care conține atât un panou de mesaje, cât și un buton „Trimite” pentru a inițializa introducerea unui mesaj nou și - Un server websockets care ascultă mesajele noi emise de clienții websocket.
Pe partea clientului, avem o pagină HTML cu câțiva handlere configurate, unul pentru evenimentul de clic pe butonul „Trimite”, care preia mesajul de intrare și îl trimite pe websocket și altul care ascultă mesajele noi primite. pe clientul websockets (adică mesajele trimise de alți utilizatori, pe care serverul dorește acum să le afișeze clientul).
Când unul dintre clienți postează un mesaj, iată ce se întâmplă:
- Browserul prinde clic pe butonul „Trimite” printr-un handler JavaScript, preia valoarea din câmpul de intrare (adică textul mesajului) și emite un mesaj websocket folosind clientul websocket conectat la serverul nostru (inițializat la inițializarea paginii web).
- Componenta de pe partea serverului a conexiunii websocket primește mesajul și îl redirecționează către toți ceilalți clienți conectați folosind metoda de difuzare.
- Toți clienții primesc noul mesaj ca mesaj push printr-o componentă websockets pe partea clientului care rulează în pagina web. Apoi preiau conținutul mesajului și actualizează pagina web în loc, adăugând noul mesaj pe panou.
Acesta este cel mai simplu exemplu. Pentru o soluție mai robustă, ați putea folosi un cache simplu bazat pe magazinul Redis. Sau într-o soluție și mai avansată, o coadă de mesaje pentru a gestiona rutarea mesajelor către clienți și un mecanism de livrare mai robust care poate acoperi pierderile temporare de conexiune sau stocarea mesajelor pentru clienții înregistrați în timp ce aceștia sunt offline. Dar, indiferent de îmbunătățirile pe care le aduceți, Node.js va funcționa în continuare sub aceleași principii de bază: reacționarea la evenimente, gestionarea multor conexiuni simultane și menținerea fluidității în experiența utilizatorului.

API PE SUPRA UNUI OBIECT DB
Deși Node.js strălucește cu adevărat cu aplicațiile în timp real, este o potrivire destul de naturală pentru expunerea datelor din DB-uri obiect (de exemplu, MongoDB). Datele stocate JSON permit lui Node.js să funcționeze fără nepotrivirea impedanței și conversia datelor.
De exemplu, dacă utilizați Rails, ați converti de la JSON la modele binare, apoi le-ați expune înapoi ca JSON prin HTTP atunci când datele sunt consumate de Backbone.js, Angular.js etc., sau chiar de jQuery AJAX simplu. apeluri. Cu Node.js, puteți pur și simplu să vă expuneți obiectele JSON cu un API REST pentru ca clientul să le consume. În plus, nu trebuie să vă faceți griji cu privire la conversia între JSON și orice altceva atunci când citiți sau scrieți din baza de date (dacă utilizați MongoDB). În concluzie, puteți evita necesitatea conversiilor multiple utilizând un format uniform de serializare a datelor pe client, server și bază de date.
INTRARI IN COADA
Dacă primiți o cantitate mare de date concurente, baza de date poate deveni un blocaj. După cum este descris mai sus, Node.js poate gestiona cu ușurință conexiunile concurente. Dar pentru că accesul la baza de date este o operațiune de blocare (în acest caz), avem probleme. Soluția este să recunoașteți comportamentul clientului înainte ca datele să fie cu adevărat scrise în baza de date.
Cu această abordare, sistemul își menține capacitatea de răspuns la o sarcină mare, ceea ce este deosebit de util atunci când clientul nu are nevoie de confirmarea fermă a scrierii de succes a datelor. Exemplele tipice includ: înregistrarea sau scrierea datelor de urmărire a utilizatorilor, procesate în loturi și care nu sunt utilizate decât o dată ulterioară; precum și operațiuni care nu trebuie să fie reflectate instantaneu (cum ar fi actualizarea numărului de „Like-uri” pe Facebook) în care eventuala consistență (atât de des folosită în lumea NoSQL) este acceptabilă.
Datele sunt puse în coadă printr-un fel de infrastructură de stocare în cache sau de așteptare a mesajelor - cum ar fi RabbitMQ sau ZeroMQ - și digerate printr-un proces separat de scriere în loturi a bazei de date sau servicii backend de procesare intensivă, scrise pe o platformă mai performantă pentru astfel de sarcini. Comportament similar poate fi implementat cu alte limbaje/cadre, dar nu pe același hardware, cu același debit ridicat și menținut.
Pe scurt: cu Node, puteți împinge în lateral scrierile de bază de date și le puteți trata mai târziu, procedând ca și cum ar fi reușit.
STREAMING DE DATE
În platformele web mai tradiționale, cererile și răspunsurile HTTP sunt tratate ca un eveniment izolat; de fapt, sunt de fapt fluxuri. Această observație poate fi utilizată în Node.js pentru a construi câteva caracteristici interesante. De exemplu, este posibil să procesăm fișiere în timp ce acestea sunt încă în curs de încărcare, deoarece datele vin printr-un flux și le putem procesa online. Acest lucru se poate face pentru codificarea audio sau video în timp real și pentru proxy între diferite surse de date (vezi secțiunea următoare).
PROXY
Node.js este utilizat cu ușurință ca proxy pe partea de server, unde poate gestiona o cantitate mare de conexiuni simultane într-un mod neblocant. Este util în special pentru a oferi servicii proxy diferite cu timpi de răspuns diferiți sau pentru a colecta date din mai multe puncte sursă.
Un exemplu: luați în considerare o aplicație pe partea de server care comunică cu resurse terțe, care extrag date din diferite surse sau care stochează active precum imagini și videoclipuri în servicii cloud terțe.
Deși există servere proxy dedicate, utilizarea Node poate fi utilă dacă infrastructura dvs. de proxy este inexistentă sau dacă aveți nevoie de o soluție pentru dezvoltarea locală. Prin aceasta, vreau să spun că ai putea construi o aplicație pe partea client cu un server de dezvoltare Node.js pentru active și solicitări API de proxy/stubbing, în timp ce în producție te-ai ocupa de astfel de interacțiuni cu un serviciu proxy dedicat (nginx, HAProxy etc. .).
BROKERAGE - TABLOUL DE BORD AL COMERCITORULUI DE ACȚIUNI
Să revenim la nivelul aplicației. Un alt exemplu în care software-ul desktop domină, dar ar putea fi înlocuit cu ușurință cu o soluție web în timp real este software-ul de tranzacționare al brokerilor, folosit pentru a urmări prețurile acțiunilor, pentru a efectua calcule/analize tehnice și pentru a crea grafice/diagrame.
Trecerea la o soluție web în timp real ar permite brokerilor să schimbe cu ușurință stațiile de lucru sau locurile de muncă. În curând, s-ar putea să începem să-i vedem pe plaja din Florida... sau Ibiza... sau Bali.
TABLA DE BORD DE MONITORIZARE A APLICATIEI
Un alt caz obișnuit de utilizare în care Node-cu-web-socket-uri se potrivește perfect: urmărirea vizitatorilor site-ului și vizualizarea interacțiunilor acestora în timp real.
S-ar putea să adunați statistici în timp real de la utilizatorul dvs. sau chiar să le mutați la nivelul următor prin introducerea de interacțiuni direcționate cu vizitatorii dvs. prin deschiderea unui canal de comunicare atunci când aceștia ajung la un anumit punct al canalului dvs. (Dacă sunteți interesat, această idee este deja produsă de CANDDi.)
Imaginează-ți cum ți-ai putea îmbunătăți afacerea dacă ai ști ce fac vizitatorii tăi în timp real, dacă ai putea vizualiza interacțiunile lor. Cu prizele bidirecționale în timp real ale Node.js, acum puteți.
TABLA DE BORD DE MONITORIZARE A SISTEMULUI
Acum, să vizităm partea de infrastructură a lucrurilor. Imaginați-vă, de exemplu, un furnizor SaaS care dorește să ofere utilizatorilor săi o pagină de monitorizare a serviciilor, cum ar fi pagina de stare a GitHub. Cu bucla de evenimente Node.js, putem crea un tablou de bord puternic bazat pe web, care verifică stările serviciilor într-o manieră asincronă și transmite date către clienți folosind websocket-uri.
Atât stările interne (intra-companie), cât și cele ale serviciilor publice pot fi raportate în direct și în timp real folosind această tehnologie. Împingeți această idee puțin mai departe și încercați să vă imaginați un Centru de operațiuni de rețea (NOC) care monitorizează aplicațiile într-un operator de telecomunicații, un furnizor de cloud/rețea/găzduire sau o instituție financiară, toate rulate pe stiva web deschisă susținută de Node.js și websocket-uri. în loc de Java și/sau Java Applets.
Unde poate fi folosit Node.js
APLICAȚII WEB PENTRU SERVER
Node.js cu Express.js poate fi folosit și pentru a crea aplicații web clasice pe partea de server. Cu toate acestea, deși este posibil, această paradigmă cerere-răspuns în care Node.js ar transporta HTML redat nu este cel mai tipic caz de utilizare. Există argumente pentru și împotriva acestei abordări. Iată câteva fapte de luat în considerare:
Pro:
- Dacă aplicația dvs. nu are un calcul intensiv CPU, o puteți construi în Javascript de sus în jos, chiar și până la nivelul bazei de date dacă utilizați DB de obiecte de stocare JSON precum MongoDB. Acest lucru ușurează în mod semnificativ dezvoltarea (inclusiv angajarea).
- Crawlerele primesc un răspuns HTML redat complet, care este mult mai prietenos cu SEO decât, de exemplu, o aplicație cu o singură pagină sau o aplicație websockets rulată pe Node.js.
Contra:
- Orice calcul intensiv CPU va bloca receptivitatea Node.js, așa că o platformă cu fire de execuție este o abordare mai bună. Alternativ, puteți încerca să extindeți calculul [*].
- Utilizarea Node.js cu o bază de date relațională este încă destul de dificilă (a se vedea mai jos pentru mai multe detalii). Faceți-vă o favoare și alegeți orice alt mediu precum Rails, Django sau ASP.Net MVC dacă încercați să efectuați operațiuni relaționale.
Unde nu ar trebui folosit Node.js
APLICAȚIE WEB LATEA SERVERULUI CU O DB RELAȚIONALĂ ÎN SPATE
Comparând Node.js cu Express.js cu Ruby on Rails, de exemplu, a existat o decizie curată în favoarea acestuia din urmă când era vorba de accesarea bazelor de date relaționale precum PostgreSQL, MySQL și Microsoft SQL Server.
Instrumentele DB relaționale pentru Node.js erau încă în stadiile incipiente. Pe de altă parte, Rails oferă automat configurarea accesului la date imediat de la pachet, împreună cu instrumente de asistență pentru migrarea schemelor DB și alte Gems. Rails și cadrele sale similare au implementări mature și dovedite pentru nivelul de acces la date Active Record sau Data Mapper.[*]
Dar lucrurile s-au schimbat. Sequelize, TypeORM și Bookshelf au parcurs un drum lung spre a deveni soluții ORM mature. De asemenea, ar putea merita să verificați Join Monster dacă doriți să generați SQL din interogări GraphQL.
COMPUTARE/PROCESARE GRE LA SERVER
Când vine vorba de calcule grele, Node.js nu este cea mai bună platformă din jur. Nu, cu siguranță nu doriți să construiți un server de calcul Fibonacci în Node.js. În general, orice operațiune intensivă a procesorului anulează toate beneficiile de debit oferite de Node cu modelul său de I/O neblocant, bazat pe evenimente, deoarece orice solicitare primită va fi blocată în timp ce firul de execuție este ocupat cu strângerea numerelor - presupunând că încercați pentru a vă rula calculele în aceeași instanță Node cu care răspundeți la solicitări.
După cum sa menționat anterior, Node.js are un singur thread și folosește doar un singur nucleu CPU. Când vine vorba de adăugarea concurenței pe un server cu mai multe nuclee, echipa de bază Node efectuează o anumită muncă sub forma unui modul de cluster [ref: http://nodejs.org/api/cluster.html]. De asemenea, puteți rula mai multe instanțe de server Node.js destul de ușor în spatele unui proxy invers prin nginx.
Cu clustering, ar trebui să descărcați în continuare toate calculele grele în procesele de fundal scrise într-un mediu mai adecvat pentru asta și să le faceți să comunice prin intermediul unui server de coadă de mesaje precum RabbitMQ.
Chiar dacă procesarea în fundal ar putea fi rulată inițial pe același server, o astfel de abordare are potențialul de scalabilitate foarte mare. Aceste servicii de procesare de fundal ar putea fi distribuite cu ușurință pe servere de lucru separate, fără a fi nevoie să configurați încărcăturile de servere web frontale.
Desigur, veți folosi aceeași abordare și pe alte platforme, dar cu Node.js obțineți acel debit mare de cerințe/sec despre care am vorbit, deoarece fiecare solicitare este o sarcină mică, gestionată foarte rapid și eficient.
Concluzie
Am discutat despre Node.js de la teorie la practică, începând cu obiectivele și ambițiile sale și terminând cu punctele sale favorabile și capcanele. Când oamenii se confruntă cu probleme cu Node, aproape întotdeauna se rezumă la faptul că operațiunile de blocare sunt rădăcina tuturor relelor - 99% din abuzurile Node vin ca o consecință directă.
Rețineți: Node.js nu a fost creat niciodată pentru a rezolva problema de scalare a calculului. A fost creat pentru a rezolva problema de scalare I/O, pe care o face foarte bine.
De ce să folosiți Node.js? Dacă cazul dvs. de utilizare nu conține operațiuni intensive de CPU și nici nu accesați resurse de blocare, puteți exploata beneficiile Node.js și vă puteți bucura de aplicații de rețea rapide și scalabile. Bun venit pe web în timp real.