Il server sovraccarico è un problema insidioso per molti siti web: a prescindere dal tipo di hosting, ci sono alcune situazioni che si ripetono di frequente e che possono essere risolte a monte, con un lavoro basato su 4 fasi per individuare e risolvere i colli di bottiglia che rallentano il sistema, migliorare le prestazioni generali del server ed evitare regressioni.

I quattro step per evitare il sovraccarico del server

A guidarci in queste operazioni è un post pubblicato su web.dev a firma di Katie Hempenius, software engineer presso Google, che segnala subito quali sono i quattro passaggi in cui si declina il lavoro:

  1. Valuta, ovvero determinare il collo di bottiglia che sta impattando sul server.
  2. Stabilizza, vale a dire implementare soluzioni rapide per mitigare l’impatto.
  3. Migliora: per aumentare e ottimizzare le capacità del server.
  4. Monitora: utilizzare strumenti automatizzati per aiutare a prevenire problemi futuri.

La fase di analisi del problema

In ingegneria (e poi anche in informatica) un collo di bottiglia (a volte anche col d’oca o in inglese bottleneck) si verifica quando un singolo componente vincola e influenza in modo pesante le prestazioni di un sistema o le sue capacità.

Quali sono i colli di bottiglia di un server

Per un sito, in caso di traffico che sovraccarica il server possono diventare dei colli di bottiglia la CPU, il network, la memoria o disk I/O; identificare quale di questi è il collo di bottiglia consente di concentrare gli sforzi sugli interventi per mitigare il danno e risolverlo. I colli di bottiglia della CPU e della rete sono quelli più rilevanti durante un picco di traffico per la maggior parte dei siti, quindi ci concentriamo principalmente su di loro.

  • CPU: un utilizzo della CPU costantemente al di sopra dell’80% deve essere studiato e corretto. Le prestazioni del server spesso peggiorano quando l’utilizzo della CPU raggiunge una soglia dell’80-90% e ciò diventa ancora più notevole avvicinandosi al 100%.

L’utilizzo della CPU per soddisfare una singola richiesta è trascurabile, ma farlo nella scala riscontrata durante i picchi di traffico a volte può sopraffare un server. Scaricare servizi su altre infrastrutture, ridurre operazioni dispendiose e limitare la quantità di richieste può ridurre l’utilizzo della CPU.

  • Network: durante i periodi di traffico intenso, la capacità di trasmissione della rete che serve a soddisfare le richieste degli utenti può superare il limiti. Alcuni siti, a seconda dell’hosting provider, possono anche superare i limiti relativi al trasferimento cumulativo di dati. Per rimuovere questo collo di bottiglia bisogna ridurre le dimensioni e la quantità di dati trasferiti da e verso il server.
  • Memoria: quando un sistema non ha memoria sufficiente, i dati devono essere riversati su disco per l’archiviazione. L’accesso al disco è molto meno rapido rispetto all’accesso alla memoria e questo può rallentare un’intera applicazione. Se la memoria diviene completamente esausta, può provocare errori di tipo Out of Memory (OOM). La regolazione dell’allocazione della memoria, la correzione delle perdite di memoria e l’aggiornamento della memoria possono rimuovere questo collo di bottiglia.
  • Disk I/O: la velocità con cui i dati possono essere letti o scritti dal disco è limitata dal disco stesso. Se l’I/O del disco è un collo di bottiglia, l’aumento della quantità di dati immagazzinati nella memoria può mitigare questo problema (a scapito di un maggiore utilizzo della memoria); se non funziona, potrebbe essere necessario fare un upgrade dei dischi.

 

Come identificare i colli di bottiglia

Eseguire il comando top di Linux sul server interessato è un buon punto di partenza per l’analisi dei colli di bottiglia; se disponibile, possiamo integrarlo con dati storici del provider di hosting o con altri strumenti di monitoraggio.

La fase di stabilizzazione

Un server sovraccarico può rapidamente portare a fallimenti a cascata in altre parti del sistema, e quindi è importante stabilizzare il server prima di tentare di apportare modifiche più significative.

Il rate limiting protegge l’infrastruttura limitando il numero di richieste in arrivo, un intervento sempre più importante quando le prestazioni del server diminuiscono: all’allungarsi dei tempi di risposta, gli utenti tendono ad aggiornare la pagina in modo aggressivo, aumentando ulteriormente il carico del server.

Rifiutare una richiesta è relativamente poco dispendioso, ma il modo migliore per proteggere il server è gestire il rate limiting a monte, ad esempio tramite un bilanciamento del carico, un proxy inverso o una CDN.

HTTP Caching

Secondo Hempenius, bisognerebbe cercare modi per memorizzare in modo più aggressivo i contenuti nella cache: se una risorsa può essere fornita da una cache HTTP (che si tratti della cache del browser o di una CDN), non è necessario richiederla dal server di origine, cosa che riduce il carico del server.

Gli header http come Cache-Control, Expires e Tag indicano come una risorsa deve essere memorizzata da una cache HTTP: il controllo e la correzione di queste intestazioni migliorerà la caching.

Anche i service workers possano essere utilizzati per la caching, ma utilizzano una cache separata e rappresentano una integrazione, anziché una sostituzione, per una corretta caching HTTP, e quindi, in caso di server sovraccarico, gli sforzi dovrebbero essere focalizzati sull’ottimizzazione della memorizzazione nella cache HTTP.

Come diagnosticare e risolvere i problemi

Per intervenire su questo aspetto, eseguiamo Google Lighthouse e concentriamoci sull’audit Uses inefficient cache policy on static assets per visualizzare un elenco di risorse con tempo di vita (TTL) breve o medio, valutando se aumentare il TTL di ogni risorsa. Come consiglio approssimativo, la Googler spiega che:

  • Le risorse statiche devono essere memorizzate nella cache con un TTL lungo (1 anno).
  • Le risorse dinamiche devono essere memorizzate nella cache con un breve TTL (3 ore).

La correzione si può attuare impostando la direttiva max-age nell’header Cache-Control sul numero appropriato di secondi, che è solo una delle molte direttive e intestazioni che influenzano il comportamento della caching dell’applicazione.

La strategia del Graceful Degradation

Il decadimento parziale è una strategia basata sulla riduzione temporanea di funzionalità per eliminare il carico in eccesso da un sistema. Questo concetto può essere applicato in molti modi diversi: ad esempio, servire una pagina di testo statica invece di un’applicazione completa, disabilitare la ricerca o restituire meno risultati di ricerca o disabilitare alcune funzionalità dispendiose o non essenziali. L’importante è concentrarsi su funzionalità che possono essere rimosse in modo semplice e sicuro con un impatto minimo sul business.

La fase dei miglioramenti

Molteplici sono i suggerimenti per implementare e ottimizzare le capacità del server; in particolare, Katie Hempenius individua almeno cinque aree verso cui concentrare l’attenzione.

1.      Utilizzare una rete di distribuzione di contenuti (CDN)

Il servizio di risorse statiche può essere scaricato dal server su una rete di distribuzione dei contenuti (CDN), riducendo così il carico. La funzione principale di una rete CDN è fornire rapidamente contenuti agli utenti attraverso una vasta rete di server situati in loro prossimità, ma la maggior parte delle CDN offre anche funzionalità aggiuntive relative alle prestazioni come compressione, bilanciamento del carico e ottimizzazione dei supporti.

2.      Ridimensionare le risorse di calcolo

La decisione di ridimensionare le risorse di calcolo dovrebbe essere presa con cura: anche se spesso è necessario, farlo prematuramente può generare “inutili complessità architettoniche e costi finanziari”.

Un elevato Time To First Byte (TTFB) può indicare che un server si sta avvicinando alla sua capacità massima, e uno strumento di monitoraggio consente di valutare in modo più preciso l’utilizzo della CPU: se il livello attuale o previsto supera l’80%, è consigliabile aumentare i server.

Aggiungere un load balancer consente di distribuire il traffico su più server, instradando il traffico a quello più appropriato; i provider di servizi cloud offrono i propri sistemi di bilanciamento del carico, oppure è possibile configurare il proprio utilizzando HAProxy o NGINX, aggiungendo poi gli altri server.

La maggior parte dei provider cloud offre il ridimensionamento automatico, che funziona in parallelo al bilanciamento del carico, modificando automaticamente le risorse di calcolo in alto e in basso in base alla domanda in un determinato momento.

Tuttavia, Hempenius sottolinea che non è uno strumento magico: ci vuole tempo affinché le nuove istanze siano online e richiede una configurazione significativa. A causa della sua complessità aggiuntiva, è necessario considerare prima una configurazione basata sul più semplice bilanciamento del carico.

3.      Abilita compressione

Le risorse text-based devono essere compresse usando gzip o brotli, che ne possono ridurre le dimensioni di trasferimento del 70% circa.

La compressione può essere abilitata aggiornando la configurazione del server.

4.      Ottimizza immagini e contenuti multimediali

Per molti siti, le immagini rappresentano il carico maggiore in termini di dimensioni dei file e l’ottimizzazione delle immagini può ridurre rapidamente e significativamente le dimensioni di un sito, come già dicevamo in un altro approfondimento.

Lighthouse ha una varietà di audit che segnalano le potenziali ottimizzazioni su queste risorse, o in alternativa si può utilizzare DevTools per identificare i file più grandi come le hero images (che probabilmente necessitano di interventi di riduzione).

In linea di massima, Hempenius suggerisce una veloce checklist:

  • Dimensioni: le immagini non devono essere più grandi del necessario.
  • Compressione: in linea generale, un livello di qualità di 80-85 avrà un effetto minimo sulla qualità dell’immagine ma riduce del 30-40% le dimensioni del file.
  • Formato: usa JPEG per le foto anziché PNG; usa MP4 per contenuti animati anziché GIF.

Più in generale, si potrebbe considerare la possibilità di impostare dei CDN per immagini, progettati per servire e ottimizzare le immagini e scaricare il servizio dal server di origine. L’impostazione di tali CDN è semplice, ma richiede l’aggiornamento degli URL delle immagini esistenti per puntare al nuovo indirizzo.

5.      Minimizza JS e CSS

Il minify rimuove i caratteri non necessari da JavaScript e CSS. Un intervento rapido consiste nel minimizzare solo JavaScript (in genere più consistente sui siti rispetto ai CSS) per avere subito un impatto maggiore.

La fase del monitoraggio

Gli strumenti di monitoraggio del server forniscono raccolta dati, dashboard e avvisi relativi alle prestazioni del server, e il loro utilizzo può aiutare a prevenire e mitigare i futuri problemi di prestazioni.

Ci sono alcune metriche che aiutano a rilevare in modo sistematico e accurato i problemi; ad esempio, il tempo di risposta del server (latenza) funziona particolarmente bene per questo, perché rileva un’ampia varietà di problemi e si correla direttamente con la user experience.