Cos'è Node.js? Spiegazione del runtime JavaScript

Scalabilità, latenza e velocità effettiva sono indicatori chiave delle prestazioni per i server Web. Mantenere bassa la latenza e il throughput elevato durante la scalabilità verticale e orizzontale non è facile. Node.js è un ambiente di runtime JavaScript che raggiunge una bassa latenza e un throughput elevato adottando un approccio "non bloccante" per servire le richieste. In altre parole, Node.js non spreca tempo o risorse in attesa del ritorno delle richieste di I / O.

Nell'approccio tradizionale alla creazione di server Web, per ogni richiesta o connessione in arrivo il server genera un nuovo thread di esecuzione o addirittura esegue il fork di un nuovo processo per gestire la richiesta e inviare una risposta. Concettualmente, questo ha perfettamente senso, ma in pratica comporta molte spese generali.

Sebbene la generazione di thread incorra in meno memoria e overhead della CPU rispetto ai processi di fork , può comunque essere inefficiente. La presenza di un numero elevato di thread può far sì che un sistema con carichi pesanti impieghi cicli preziosi per la pianificazione dei thread e il cambio di contesto, che aggiunge latenza e impone limiti alla scalabilità e alla velocità effettiva.

Node.js ha un approccio diverso. Esegue un ciclo di eventi a thread singolo registrato con il sistema per gestire le connessioni e ogni nuova connessione attiva una funzione di callback JavaScript . La funzione di callback può gestire le richieste con chiamate I / O non bloccanti e, se necessario, può generare thread da un pool per eseguire operazioni di blocco o ad alta intensità di CPU e per bilanciare il carico tra i core della CPU. L'approccio di Node al ridimensionamento con funzioni di callback richiede meno memoria per gestire più connessioni rispetto alle architetture più competitive che scalano con i thread, inclusi Apache HTTP Server, i vari server di applicazioni Java, IIS e ASP.NET e Ruby on Rails.

Node.js risulta essere abbastanza utile per le applicazioni desktop oltre ai server. Si noti inoltre che le applicazioni Node non sono limitate al puro JavaScript. È possibile utilizzare qualsiasi linguaggio che trasporta in JavaScript, ad esempio TypeScript e CoffeeScript. Node.js incorpora il motore JavaScript V8 di Google Chrome, che supporta la sintassi ECMAScript 2015 (ES6) senza bisogno di un transpiler da ES6 a ES5 come Babel.

Gran parte dell'utilità di Node proviene dalla sua ampia libreria di pacchetti, accessibile dal npmcomando. NPM, il gestore di pacchetti Node, fa parte dell'installazione standard di Node.js, sebbene abbia un proprio sito web.

Un po 'di cronologia JavaScript

Nel 1995 Brendan Eich, allora un appaltatore di Netscape, creò il linguaggio JavaScript per funzionare nei browser Web, in 10 giorni, come si racconta. JavaScript era inizialmente concepito per abilitare le animazioni e altre manipolazioni del DOM (Document Object Model) del browser. Poco dopo è stata introdotta una versione di JavaScript per Netscape Enterprise Server.

Il nome JavaScript è stato scelto per scopi di marketing, poiché il linguaggio Java di Sun era ampiamente pubblicizzato all'epoca. In effetti, il linguaggio JavaScript era in realtà basato principalmente sui linguaggi Scheme e Self, con semantica simile a Java.

Inizialmente, molti programmatori hanno respinto JavaScript come inutile per il "lavoro reale" perché il suo interprete ha eseguito un ordine di grandezza più lentamente rispetto ai linguaggi compilati. La situazione è cambiata quando diversi sforzi di ricerca volti a rendere JavaScript più veloce hanno iniziato a dare i loro frutti. Soprattutto, il motore JavaScript V8 di Google Chrome open source, che esegue la compilazione just-in-time, l'inlining e l'ottimizzazione del codice dinamico, può effettivamente superare il codice C ++ per alcuni carichi e superare Python per la maggior parte dei casi d'uso.

La piattaforma Node.js basata su JavaScript è stata introdotta nel 2009, da Ryan Dahl, per Linux e MacOS, come alternativa più scalabile al server HTTP Apache. NPM, scritto da Isaac Schlueter, è stato lanciato nel 2010. Una versione Windows nativa di Node.js ha debuttato nel 2011.

Joyent ha posseduto, governato e sostenuto lo sforzo di sviluppo di Node.js per molti anni. Nel 2015, il progetto Node.js è stato consegnato alla Fondazione Node.js ed è diventato governato dal comitato tecnico direttivo della fondazione. Node.js è stato anche accolto come un progetto collaborativo della Linux Foundation. Nel 2019, la Node.js Foundation e la JS Foundation si sono fuse per formare la OpenJS Foundation.

Architettura di base di Node.js

Ad alto livello, Node.js combina il motore JavaScript V8 di Google, un ciclo di eventi non bloccante a thread singolo e un'API di I / O di basso livello. Il codice di esempio semplificato mostrato di seguito illustra il modello di server HTTP di base, utilizzando le funzioni freccia ES6 (funzioni Lambda anonime dichiarate utilizzando l'operatore freccia grasso =>) per i callback.

L'inizio del codice carica il modulo HTTP, imposta la hostnamevariabile del server su localhost(127.0.0.1) e imposta la portvariabile su 3000. Quindi crea un server e una funzione di callback, in questo caso una funzione freccia grossa che restituisce sempre la stessa risposta a qualsiasi richiesta: statusCode200 (successo), tipo di contenuto testo normale e una risposta di testo di ”Hello World\n”. Infine, dice al server di ascoltare sulla localhostporta 3000 (tramite un socket) e definisce un callback per stampare un messaggio di log sulla console quando il server ha iniziato ad ascoltare. Se esegui questo codice in un terminale o una console utilizzando il nodecomando e quindi vai a localhost: 3000 utilizzando qualsiasi browser Web sulla stessa macchina, vedrai "Hello World" nel tuo browser. Per arrestare il server, premi Control-C nella finestra del terminale.

Notare che ogni chiamata effettuata in questo esempio è asincrona e non bloccante. Le funzioni di callback vengono richiamate in risposta agli eventi. Il createServercallback gestisce un evento di richiesta del client e restituisce una risposta. Il listencallback gestisce l' listeningevento.

La libreria Node.js

Come puoi vedere sul lato sinistro della figura sotto, Node.js ha una vasta gamma di funzionalità nella sua libreria. Il modulo HTTP che abbiamo utilizzato in precedenza nel codice di esempio contiene classi sia client che server, come puoi vedere sul lato destro della figura. La funzionalità del server HTTPS che utilizza TLS o SSL risiede in un modulo separato.

Un problema intrinseco con un ciclo di eventi a thread singolo è la mancanza di ridimensionamento verticale, poiché il thread del ciclo di eventi utilizzerà solo un singolo core della CPU. Nel frattempo, i chip CPU moderni spesso espongono otto o più core e i rack di server moderni spesso hanno più chip CPU. Un'applicazione a thread singolo non sfrutterà appieno gli oltre 24 core in un robusto server rack.

Puoi risolverlo, sebbene richieda una programmazione aggiuntiva. Per cominciare, Node.js può generare processi figlio e mantenere le pipe tra il genitore e i figli, in modo simile al modo in cui funziona la popen(3)chiamata di sistema , utilizzando child_process.spawn() i metodi correlati.

Il modulo cluster è ancora più interessante del modulo del processo figlio per la creazione di server scalabili. Il cluster.fork()metodo genera processi di lavoro che condividono le porte del server del genitore, utilizzando child_process.spawn()sotto le copertine. Il master del cluster distribuisce le connessioni in ingresso tra i suoi worker utilizzando, per impostazione predefinita, un algoritmo round-robin sensibile ai carichi del processo di lavoro.

Tieni presente che Node.js non fornisce la logica di routing. Se desideri mantenere lo stato tra le connessioni in un cluster, dovrai mantenere la sessione e gli oggetti di accesso in un luogo diverso dalla RAM di lavoro.

L'ecosistema del pacchetto Node.js

Il registro NPM ospita più di 1,2 milioni di pacchetti di codice Node.js riutilizzabile e gratuito, il che lo rende il registro software più grande al mondo. Si noti che la maggior parte dei pacchetti NPM (essenzialmente cartelle o elementi del registro di NPM contenenti un programma descritto da un file package.json) contengono più moduli (programmi caricati con requireistruzioni). È facile confondere i due termini, ma in questo contesto hanno significati specifici e non dovrebbero essere scambiati.

NPM può gestire pacchetti che sono dipendenze locali di un particolare progetto, nonché strumenti JavaScript installati a livello globale. Quando viene utilizzato come gestore delle dipendenze per un progetto locale, NPM può installare, in un unico comando, tutte le dipendenze di un progetto tramite il file package.json. Quando viene utilizzato per installazioni globali, NPM richiede spesso privilegi di sistema (sudo).

Non è necessario utilizzare la riga di comando di NPM per accedere al registro pubblico di NPM. Altri gestori di pacchetti come Yarn di Facebook offrono esperienze lato client alternative. È anche possibile cercare e cercare pacchetti utilizzando il sito Web di NPM.

Perché dovresti utilizzare un pacchetto NPM? In molti casi, l'installazione di un pacchetto tramite la riga di comando di NPM è la più rapida e conveniente per ottenere l'ultima versione stabile di un modulo in esecuzione nel proprio ambiente ed è in genere meno impegnativa rispetto alla clonazione del repository di origine e alla creazione di un'installazione dal repository. Se non si desidera la versione più recente, è possibile specificare un numero di versione per NPM, che è particolarmente utile quando un pacchetto dipende da un altro pacchetto e potrebbe non funzionare con una versione più recente della dipendenza.

Ad esempio, il framework Express, un framework per applicazioni Web Node.js minimale e flessibile, fornisce un robusto set di funzionalità per la creazione di applicazioni Web a pagina singola, multipagina e ibrida. Sebbene il repository Expresscode facilmente clonabile risieda su //github.com/expressjs/express e la documentazione di Express si trovi su //expressjs.com/, un modo rapido per iniziare a utilizzare Express è installarlo in uno sviluppo funzionante locale già inizializzato directory con il npmcomando, ad esempio:

$ npm install express: salva

L' —saveopzione, che è attualmente attiva per impostazione predefinita in NPM 5.0 e versioni successive, indica al gestore dei pacchetti di aggiungere il modulo Express all'elenco delle dipendenze nel file package.json dopo l'installazione.

Un altro modo rapido per iniziare a utilizzare Express è installare il generatore di eseguibili aexpress(1) livello globale e quindi utilizzarlo per creare l'applicazione localmente in una nuova cartella di lavoro:

$ npm install -g express-generator @ 4

$ express / tmp / foo && cd / tmp / foo

Fatto ciò, è possibile utilizzare NPM per installare tutte le dipendenze necessarie e avviare il server, in base al contenuto del file package.json creato dal generatore:

Installazione di $ npm

$ npm inizio

È difficile scegliere i punti salienti tra gli oltre milioni di pacchetti in NPM, ma alcune categorie si distinguono. Express è l'esempio più antico e più importante di framework Node.js. Un'altra grande categoria nel repository NPM sono le utilità di sviluppo JavaScript, tra cui browserify, un bundler di moduli; bower, il gestore dei pacchetti del browser; grunt, il task runner di JavaScript; e gulp, il sistema di generazione in streaming. Infine, una categoria importante per gli sviluppatori Node.js aziendali sono i client di database, di cui ce ne sono più di 8.000, inclusi i moduli popolari come redis, mongoose, firebase e pg, il client PostgreSQL.

Per riassumere, Node.js è un ambiente di runtime JavaScript multipiattaforma per server e applicazioni. Si basa su un ciclo di eventi a thread singolo non bloccante, sul motore JavaScript V8 di Google Chrome e su un'API di I / O di basso livello. Varie tecniche, incluso il modulo cluster, consentono alle app Node.js di scalare oltre un singolo core della CPU. Oltre alle sue funzionalità di base, Node.js ha ispirato un ecosistema di oltre un milione di pacchetti che sono registrati e con versione nel repository NPM e possono essere installati utilizzando la riga di comando NPM o un'alternativa come Yarn.