Se hai notato che Nginx è in grado di inviare le richieste web ad un altro processo (proxying), potresti anche aver capito che Nginx può funzionare come load balancer. Può infatti distribuire richieste web ad altri server o processi.

Prima di iniziare a parlare di load balancers, ecco alcune considerazioni:

– Le sessioni utente possono persistere attraverso connessioni a diversi server (o no)
– Vengono soddisfatte le regole di sicurezza SSL (i server che hanno certificato/i SSL)
– I file caricati dagli utenti sono situati in uno storage centralizzato e non nel web server su cui si connette l’utente
– La tua applicazione è proxy-aware (ottiene il corretto indirizzo IP del client, la porta e il protocollo)

Dopo esserti assicurato che l’applicazione web è configurata per un ambiente distribuito, puoi decidere di applicare una strategia di load balancing.
Queste alcune delle strategie offerte da Nginx:

Round Robin – Nginx scambia i server per soddisfare la richiesta e per fare in modo che siano definiti
Least Connections – Una richiesta è assegnata al server con meno connessioni (e presumibilmente meno sovraccaricato)
Sessioni Ip-Hash/Sticky – L’indirizzo IP del Client è hashato. Il risultante hash viene utilizzato per determinare a quale server inviare la richiesta. Ciò rende le sessioni utente “sticky”. Le conseguenti richieste da un utente specifico, passano sempre attraverso lo stesso server. In questo modo si elude il problema del comportamento delle sessioni utente in un ambiente distribuito.
Peso – Con qualsiasi di queste strategie, puoi assegnare un peso al server. E’ più probabile che i server più pesanti siano selezionati come server per una richiesta. E’ ottimo metodo se c’è bisogno di utilizzare un server potente più spesso, o per utilizzare un server con specs/software nuovi o sperimentali.

Configurazione

Una configurazione base è molto semplice. Mettiamo di avere tre processi node.js attivi e vogliamo distribuire tra loro le richieste. Possiamo configurare il nostro Nginx in questo modo:

# Definiamo i server su cui bilanciare il carico
upstream app_example {
    least_conn;                 # Usiamo la strategia di balancing Least Connections
    server 127.0.0.1:9000;      # NodeJS Server 1
    server 127.0.0.1:9001;      # NodeJS Server 2
    server 127.0.0.1:9002;      # NodeJS Server 3
}
 
# Definiamo il server Nginx
# Questo proxa in particolare ogni directory non-statica
server {
    listen 80;
    server_name example.com www.example.com;
 
    access_log /var/log/nginx/example.com-access.log;
    error_log  /var/log/nginx/example.com-error.log error;
 
    # Disabilitiamo i log per queste due risorse richieste spesso da Browser e Bot.
    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt  { log_not_found off; access_log off; }
 
    # Gestiamo i file statici così che non vengano proxati ai server NodeJS
    location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
      root /var/www;
    }
 
    # passiamo la richiesta al server node.js
    # con header corretti per far sapere che il traffico proviene dal proxy
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
 
        proxy_pass http://app_example/;
        proxy_redirect off;
 
        # Gestiamo le connessioni Web Socket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
 
# Configuriamo anche l'SSL
server {
    listen 443;
 
    # Avrete bisogno qui della vostra chiave e certificato
    # Non basta copiare e incollare
    ssl on;
    ssl_certificate     /etc/ssl/example.com/example.com.crt;
    ssl_certificate_key /etc/ssl/example.com/example.com.key;
 
    # ... Il resto qui è proprio come definito sopra ...
}

L’Nginx sopra imposta le richieste proxy a tre processi node.js locali che sono impostati per accettare richieste HTTP e servire i client.
La configurazione ricerca le location dei file statici. Nginx gestisce le richieste statiche per conto suo, senza bisogno di un’assegnazione manuale dei file statici che verranno poi gestiti dai processi Node.
Nel caso in cui sia necessario, puoi definire un ulteriore blocco di server Upstream che gestisca i file statici. In questo modo il server Nginx verrebbe utilizzato esclusivamente come load balancer.

Se desideri vedere il codice dei server Node.js che ho utilizzato per questo scopo, eccolo:

var http = require('http');
 
function serve(ip, port) {
   http.createServer(function (req, res) {
       res.writeHead(200, {'Content-Type': 'text/plain'});
       res.end("There's no place like "+ip+":"+port+"\n");
   }).listen(port, ip);
   console.log('Server running at http://'+ip+':'+port+'/');
}
 
serve('127.0.0.1', 9000);
serve('127.0.0.1', 9001);
serve('127.0.0.1', 9002);