Perché dovrei usare un Proxy inverso se il nodo.js è pronto per la produzione?

Thomas Hunter II
Thomas Hunter II

Seguire

Mar 28, 2019 · 9 min leggere

era L’anno 2012. PHP e Ruby on Rails regnavano come le supreme tecnologie lato server per il rendering di applicazioni web. Ma, un nuovo concorrente audace ha preso la comunità dalla tempesta — uno che è riuscito a gestire 1M connessioni simultanee. Questa tecnologia non era altro che Nodo.js e ha costantemente aumentato in popolarità da allora.

A differenza della maggior parte delle tecnologie concorrenti del tempo, Nodo.js è venuto con un server web integrato. Avere questo server significava che gli sviluppatori potevano ignorare una miriade di file di configurazione come php.ini e una raccolta gerarchica di
.htaccess file. Avere un server web integrato offriva anche altre comodità, come la possibilità di elaborare i file mentre venivano caricati e la facilità di implementare WebSockets.

Nodo ogni giorno.le applicazioni web basate su js gestiscono felicemente miliardi di richieste. La maggior parte delle più grandi aziende del mondo sono alimentati in qualche modo da nodo.js. Per dire quel Nodo.js è pronto per la produzione è certamente un
eufemismo. Tuttavia, c’è un consiglio che è rimasto valido dal nodo.inception di js: non si dovrebbe esporre direttamente un Nodo.js processa sul Web e dovrebbe invece nasconderlo dietro un proxy inverso. Ma, prima di esaminare i motivi per cui vorremmo utilizzare un proxy inverso, diamo prima un’occhiata a ciò che uno è.

Un proxy inverso è fondamentalmente un tipo speciale di server Web che riceve richieste, le inoltra a un altro server HTTP da qualche altra parte, riceve una risposta e inoltra la risposta al richiedente originale.

Un proxy inverso di solito non invia la richiesta esatta, però. In genere modificherà la richiesta in qualche modo. Ad esempio, se il proxy inverso vive su www.example.org:80, e sta per inoltrare la richiesta a
ex.example.org:8080, probabilmente riscriverà l’intestazione originale Host in modo che corrisponda a quella del target. Può anche modificare la richiesta in altri modi, come la pulizia di una richiesta malformata o la traduzione tra protocolli.

Una volta che il proxy inverso riceve una risposta, può quindi tradurre tale risposta in qualche modo. Ancora una volta, un approccio comune consiste nel modificare l’intestazione Host in modo che corrisponda alla richiesta originale. Anche il corpo delle richieste può essere modificato. Una modifica comune consiste nell’eseguire la compressione gzip sulla risposta. Un’altra modifica comune consiste nell’abilitare il supporto HTTPS quando il servizio sottostante parla solo HTTP.

I proxy inversi possono anche inviare richieste in arrivo a più istanze di back-end. Se un servizio è esposto a api.example.org, un proxy inverso potrebbe inoltrare le richieste a api1.internal.example.orgapi2, ecc.

Ci sono molti diversi proxy inversi là fuori. Due dei più popolari sono Nginx e HAProxy. Entrambi questi strumenti sono in grado di eseguire la compressione gzip e aggiungere il supporto HTTPS, e sono specializzati in altre aree pure. Nginx è la più popolare delle due scelte, e ha anche alcune altre
funzionalità benefiche come la possibilità di servire file statici da un filesystem, quindi lo useremo come esempio in questo articolo.

Ora che sappiamo cos’è un proxy inverso, possiamo ora esaminare perché vorremmo farne uso con Node.js.

Perché dovrei usare un proxy inverso?

La terminazione SSL è uno dei motivi più popolari per cui si utilizza un proxy inverso. Cambiare il protocollo di un’applicazione da httpahttpsrichiede un po ‘ più di lavoro rispetto all’aggiunta di uns. Nodo.js stesso è in grado di eseguire la crittografia e la decrittografia necessarie per https e può essere configurato per leggere i file di certificato necessari.

Tuttavia, la configurazione del protocollo utilizzato per comunicare con la nostra applicazione e la gestione dei certificati SSL in scadenza non è in realtà qualcosa di cui la nostra applicazione deve essere preoccupata. Controllare i certificati in una base di codice non sarebbe solo noioso, ma anche un rischio per la sicurezza. L’acquisizione di certificati da una posizione centrale all’avvio dell’applicazione ha anche i suoi rischi.

Per questo motivo è meglio eseguire la terminazione SSL al di fuori dell’applicazione, di solito all’interno di un proxy inverso. Grazie a tecnologie come certbot di Let’s Encrypt, mantenere i certificati con Nginx è facile come impostare un lavoro cron. Tale lavoro può installare automaticamente nuovi certificati e riconfigurare dinamicamente il processo Nginx. Questo è un processo molto meno dirompente quindi, ad esempio, il riavvio di ciascun Nodo.istanza dell’applicazione js.

Inoltre, consentendo a un proxy inverso di eseguire la terminazione SSL, ciò significa che solo il codice scritto dagli autori del proxy inverso ha accesso al certificato SSL privato. Tuttavia, se il tuo Nodo.l’applicazione js gestisce SSL, quindi ogni singolo modulo di terze parti utilizzato dall’applicazione, anche moduli potenzialmente dannosi, avrà accesso al certificato SSL privato.

Compressione gzip

la compressione gzip è un’altra funzionalità che dovresti scaricare dall’applicazione a un proxy inverso. i criteri di compressione gzip sono qualcosa di meglio impostato a livello di organizzazione, invece di dover specificare e configurare per ogni applicazione.

È meglio usare un po ‘ di logica quando si decide cosa gzip. Ad esempio, i file che sono molto piccoli, forse più piccoli di 1 kb, probabilmente non valgono la pena di essere compressi in quanto la versione compressa gzip a volte può essere più grande, o il sovraccarico della CPU di avere il client decomprimere il file potrebbe non valerne la pena. Inoltre, quando si tratta di dati binari, a seconda del formato potrebbe non beneficiare della compressione. gzip è anche qualcosa che non può essere semplicemente abilitato o disabilitato, richiede l’esame dell’intestazione Accept-Encoding in entrata per algoritmi di compressione compatibili.

Clustering

JavaScript è un linguaggio a thread singolo e, di conseguenza, Nodo.js è stato tradizionalmente una piattaforma server single-threaded (anche se, il supporto thread di lavoro attualmente sperimentale disponibile nel nodo.js v10 mira a cambiare questo). Ciò significa ottenere tanto throughput da un Nodo.applicazione js possibile richiede l’esecuzione di circa lo stesso numero di istanze come ci sono core della CPU.

Nodo.js viene fornito con un modulo cluster incorporato che può fare proprio questo. Le richieste HTTP in entrata verranno inoltrate a un processo master, quindi inviate ai lavoratori del cluster.

Tuttavia, il ridimensionamento dinamico dei lavoratori del cluster richiederebbe un certo sforzo. Di solito viene anche aggiunto un overhead nell’esecuzione di un nodo aggiuntivo.processo js come processo master di spedizione. Inoltre, il ridimensionamento dei processi su macchine diverse è qualcosa che cluster non può fare.

Per questi motivi a volte è meglio usare un proxy inverso per inviare richieste al nodo in esecuzione.processi js. Tali proxy inversi possono essere configurati dinamicamente per puntare a nuovi processi applicativi man mano che arrivano. In realtà, un’applicazione dovrebbe solo occuparsi di fare il proprio lavoro, non dovrebbe occuparsi della gestione di più copie e delle richieste di invio.

Enterprise Routing

Quando si ha a che fare con applicazioni web massicce, come quelle create da aziende multi-team, è molto utile avere un proxy inverso per determinare dove inoltrare le richieste. Ad esempio, le richieste effettuate a example.org/search/* possono essere indirizzate all’applicazione di ricerca interna mentre altre richieste effettuate a example.org/profile/* possono essere inviate all’applicazione del profilo interno.

Tali strumenti consentono altre potenti funzionalità come sessioni appiccicose, distribuzioni blu/verdi, test A / B, ecc. Ho lavorato personalmente in una base di codice in cui tale logica è stata eseguita all’interno dell’applicazione e questo approccio ha reso l’applicazione piuttosto difficile da mantenere.

Vantaggi in termini di prestazioni

Nodo.js è altamente malleabile. È in grado di servire risorse statiche da un filesystem, eseguire la compressione gzip con risposte HTTP, viene fornito con il supporto integrato per HTTPS e molte altre funzionalità. Ha anche la capacità di eseguire più istanze di un’applicazione ed eseguire il proprio dispacciamento delle richieste, tramite il modulo cluster.

Eppure, alla fine è nel nostro interesse lasciare che un proxy inverso gestisca queste operazioni per noi, invece di avere il nostro Nodo.applicazione js farlo. Oltre a ciascuno dei motivi sopra elencati, un altro motivo per voler fare queste operazioni al di fuori del nodo.js è dovuto all’efficienza.

La crittografia SSL e la compressione gzip sono due operazioni altamente legate alla CPU. Gli strumenti di proxy inverso dedicati, come Nginx e HAProxy, in genere eseguono queste operazioni più velocemente del nodo.js. Avere un server web come Nginx leggere il contenuto statico dal disco sarà più veloce del nodo.anche js. Anche il clustering a volte può essere più efficiente in quanto un proxy inverso come Nginx utilizzerà meno memoria e CPU di quello di un nodo aggiuntivo.processo js.

Ma, non prendere la parola per esso. Eseguiamo alcuni benchmark!

Il seguente test di carico è stato eseguito utilizzando siege. Abbiamo eseguito il comando con un valore di concorrenza di 10 (10 utenti simultanei che effettuano una richiesta) e il comando sarebbe stato eseguito fino a 20.000 iterazioni (per 200.000 richieste complessive).

Per controllare la memoria eseguiamo il comando pmap <pid> | grep total un paio di volte per tutta la durata del benchmark quindi la media dei risultati. Quando si esegue Nginx con un singolo thread di lavoro, finiscono per essere due istanze in esecuzione, una è il master e l’altra è il worker. Quindi sommiamo i due valori. Quando si esegue un Nodo.js cluster di 2 ci saranno 3 processi, uno è il master e gli altri due sono lavoratori. La colonna di memoria approssimativa nella tabella seguente è una somma totale di ciascun Nginx e Nodo.processo js per il test dato.

Ecco i risultati del benchmark:

Risultati di Benchmark

Nel node-cluster benchmark usiamo 2 addetti. Ciò significa che ci sono 3 Nodi.processi js in esecuzione: 1 master e 2 lavoratori. Nel benchmarknginx-cluster-node abbiamo 2 Nodi.processi js in esecuzione. Ogni test Nginx ha un singolo master Nginx e un singolo processo di lavoro Nginx. I benchmark implicano la lettura di un file dal disco e né Nginx né Node.js sono stati configurati per memorizzare nella cache il file in memoria.

Utilizzo di Nginx per eseguire la terminazione SSL per il nodo.js si traduce in un aumento del throughput di ~16% (da 749rps a 865rps). L’utilizzo di Nginx per eseguire la compressione gzip comporta un aumento del throughput di ~50% (da 5.047 rps a 7.590 rps). L’utilizzo di Nginx per gestire un cluster di processi ha comportato una penalizzazione delle prestazioni di ~-1% (da 8.006 rps a 7.908 rps), probabilmente a causa del sovraccarico del passaggio di una richiesta aggiuntiva sul dispositivo di rete di loopback.

Essenzialmente l’utilizzo della memoria di un singolo Nodo.il processo js è ~600 MB, mentre l’utilizzo della memoria di un processo Nginx è di circa ~50 MB. Questi possono fluttuare un po ‘ a seconda di quali caratteristiche vengono utilizzate, ad esempio, Nodo.js utilizza un ulteriore ~13 MB quando si esegue la terminazione SSL, e Nginx utilizza un ulteriore ~ 4 MB quando viene utilizzato come un verso proxy inverso che serve contenuto statico dal filesystem. Una cosa interessante da notare è che Nginx utilizza una quantità costante di memoria per tutta la sua vita. Tuttavia, Nodo.js fluttua costantemente a causa della natura di raccolta dei rifiuti di JavaScript.

Ecco le versioni del software utilizzato durante l’esecuzione di questo benchmark:

  • Nginx:1.14.2
  • Node.js: 10.15.3
  • Siege: 3.0.8

I test sono stati eseguiti su una macchina con 16GB di memoria, un i7-7500U CPU 4x2.70GHz e il kernel Linux 4.19.10. Tutti i file necessari per ricreare i benchmark di cui sopra sono disponibili qui:
IntrinsicLabs/nodejs-reverse-proxy-benchmarks.

Codice dell’applicazione semplificato

I benchmark sono belli e tutti, ma a mio parere i maggiori vantaggi di scaricare il lavoro da un Nodo.l’applicazione js a un proxy inverso è quella della semplicità del codice. Arriviamo a ridurre il numero di righe di codice applicativo imperativo potenzialmente buggato e lo scambiamo per la configurazione dichiarativa. Un sentimento comune tra gli sviluppatori è che sono più sicuri nel codice scritto da un team esterno di ingegneri — come Nginx — che nel codice scritto da soli.

Invece di installare e gestire il middleware di compressione gzip e mantenerlo aggiornato su vari nodi.progetti js possiamo invece configurarlo in un’unica posizione. Invece di spedire o scaricare certificati SSL e ri-acquisirli o riavviare i processi applicativi, possiamo invece utilizzare gli strumenti di gestione dei certificati esistenti. Invece di aggiungere condizionali alla nostra applicazione per verificare se un processo è un master o un lavoratore, possiamo scaricarlo su un altro strumento.

Un proxy inverso consente alla nostra applicazione di concentrarsi sulla logica di business e dimenticare i protocolli e la gestione dei processi.



Lascia un commento

Il tuo indirizzo email non sarà pubblicato.