Websocket, la panacea al polling

W

introduzione

Ciao Coders,

oggi vi parleremo di web socket, un protocollo di messaggistica che permette una comunicazione asincrona e full-duplex su connessione TCP. Il mondo delle applicazioni negli ultimi anni si è evoluto molto: siamo passati da client di chat desktop come Msn e Skype a chat web come Facebook. Non solo il mondo delle chat si è spostato sul web ma anche su applicazioni enterprise e critical business: in tali applicazioni il realtime è un nodo cruciale, basti pensare ad un client web di trading nel quale alcuni dati mostrati nella GUI devono essere assolutamente realtime (esempio il valore di una azione finanziaria può variare ogni secondo) o a un sistema web di monitoraggio per cui si vuole avvertire tempestivamente un utente di un determinato evento. Prima dell’avvento degli web socket una possibile soluzione per implementare applicazioni simil real-time era quello di sviluppare una sorta di polling. Ad esempio supponiamo di dover realizzare una sorta di notifica in una applicazione web; il client ogni tot secondi richiede al server se ci sono nuovi messaggi e in caso affermativo mostra il nuovo messaggio.

 

Questo meccanismo è molto inefficiente:

  • il dato non viene ricevuto in realtime;
  • spreco immenso di banda
  • overhead del server: il server si troverà constantemente bombardato dai client connessi.

Insomma se non ci siete ancora arrivati il polling e’ il MALE.

 

LIMITI HTTP

Il protocollo HTTP (Hypertext Transfer Protocol) nasce come protocollo di tipo request-response senza stato (stateless). La semplicità del protocollo HTTP lo rende molto scalabile, ma inefficiente e non adatto per le applicazioni web altamente interattive. Un altro limite del protocolo è che fu progettato per essere half-duplex, il che significa che la trasmissione dei dati è consentita in una sola direzione alla volta.

WEB-SOCKET: DEFINIZIONE

Introduciamo gli WebSocket con la definizione riportata in mozilla developer:

 Gli WebSockets sono una tecnologia avanzata che rende possibile aprire una sessione di comunicazione interattiva tra il browser dell’utente e un server. Con questa API si possono mandare messaggi al server e ricevere risposte event-driven senza doverle richiedere al server.

Tale protocollo fu standardizzato dall’IETF come RFC6455  e incluso nella specifica HTML5;  si presenta come un protocollo di messaggistica che permette una comunicazione asincrona e full-duplex su connessione TCP.  Un sistema full-duplex permette la comunicazione in entrambe le direzioni in maniera contemporanea, proprio come accade in una classica telefonata dove gli interlocutori possono  parlare ed essere ascoltati allo stesso tempo. In poche parole proprio quello che mancava al web per poter realizzare applicazioni enterprise.

WEB-SOCKET: come funzionano

Ogni connessione WebSocket ha inizio con una richiesta HTTP inviata dal client al server chiamata WebSocket handshake.

GET ws://websocket.example.italiancoders.it/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket

NB: Gli WebSocket URLs usano lo schema ws. Esiste anche wss per le connessioni websocket sicure che sono l’equivalente di HTTPS.

La WebSocket handshake request è una normale richiesta http, salvo l’inclusione negli headers dell’intestazione Upgrade che indica che il client vuole aggiornare la connessione ad un protocollo diverso, in questo caso a WebSocket. Se il server accetta l’handshake e supporta il protocollo, accetta l’aggiornamento e lo comunica tramite l’header Upgrade.

HTTP/1.1 101 WebSocket Protocol Handshake
Date: Wed, 16 Oct 2013 10:07:34 GMT
Connection: Upgrade
Upgrade: WebSocket

A questo punto l’iniziale connessione HTTP viene sostituita con una connessione WebSocket che fa uso del medesimo protocollo TCP/IP. Con gli WebSocket puoi trasferire dati senza incorrere nell’overhead associato alle tradizionali richieste HTTP. I dati vengono trasferiti tramite una WebSocket come messaggi, ognuno dei quali è costituito da uno o più frame contenente ciascuno i dati scambiati. Per garantire che il messaggio possa essere ricostruito correttamente quando raggiunge il client, ogni frame è preceduto da 4-12 byte di dati sul carico utile. L’utilizzo di questo sistema di messaggistica basato su frame consente di ridurre la quantità di dati non a carico utile trasferiti, con conseguente riduzione significativa della latenza. Tale connessione è mantenuta per ogni singolo client, permettendo di inviare in real-time dati a uno o più client quando il server lo decide. Ho sottolineato in rosso l’ultima frase perché é la vera novità e chiave vincente degli web socket. Perché fare una richiesta ogni 5 secondi per sapere se è arrivato un nuovo messaggio quando il server è in grado di avvertire il client nell’istante preciso in cuiesso arriva? I vantaggi sono molti ma i più tangibili:

  • vero real-time: unico delay che rimane è quello del trasferimento dati
  • meno overheader lato server: grazie al fatto che se,ad esempio, per due ore non ci sono nuovi messaggi il server non viene stressato da inutili richieste
  • bassa latenza e nessun spreco di banda: dobbiamo considerare che per stabilire una normale connessione HTTP devi prima stabilire una connessione TCP (SYN, SYN/ACK, ACK), quindi inviare una richiesta GET con un header piuttosto grande e infine ricevere la risposta del server (insieme a un’altra grande header). Con un WebSocket aperto si riceve invece semplicemente la risposta (nessuna richiesta necessaria) fornita con un header decisamente più piccolo: da due byte per i frame piccoli, fino a 10 byte per i frame ridicolmente grandi (nell’intervallo dei gigabyte).

Vediamo quindi come cambia il grafico del nostro esempio.

 

WEB-SOCKET: demo

Ho preparato per voi una veloce demo in Node.js clonabile dalla mia REPO GITHUB.

Ho utilizzato Express per realizzare un semplice web server e ws la libreria node che implementa gli websocket lato client/server.

SERVER.JS

var express = require('express');
var ws = require('./ws');

var app = express()

app.get('/', function (req, res) {
    res.sendFile(__dirname + '/client.html');
})

app.listen(3000, function () {
    console.log('Home Listening on port 3000!');
})

Ecco lo script che crea il webserver con express, risponde alla richiesta verso l’url / con la pagina html client.html. Inoltre viene istanziato il modulo ws (implementato in ws.js).

WS.JS

var WebSocketServer = require('ws').Server,
    wss = new WebSocketServer({port: 40999});

wss.on('connection', function (ws) {
    ws.on('message', function (message) {
        console.log('received: %s', message)
    })

    setInterval(() => {
        console.log('preparo il messaggio');
        //logiche di creazione del messagio
        const message =  { text: 'Ciao', sentAt: new Date() };
        ws.send(JSON.stringify(message));
    }, 1000);

})

Tramite ws.js viene implementata la parte websocket lato server di questa demo:

  • viene creato il websocketserver che risponde alla porta 40999
  • viene creata la callback che risponde all’evento di nuova connessione di un client
  • ogni 1000 ms viene inviato un messaggio nel websocket

 

CLIENT.HTML

<h1>
  Italian coders web socket example
</h1>
<p>
    open console to see connection.
</p>
<script>
    var ws = new WebSocket('ws://localhost:40999');

    ws.onopen = function () {
        console.log('websocket  connection stabilita...')
        ws.send('hello')
    }

    ws.onmessage = function (message) {
        console.log('Messaggio ricevuto :',message);
    }
</script>

Ecco la pagina html restituita al browser la quale include uno script js che:

  • stabilisce una connessione websocket con il server
  • instanzia una callback per l’evento che gestisce l’arrivo di un nuovo messaggio, la quale stampa nella console il messaggio ricevuto

Per i neofiti di node includo gli step di installazione:

  • clonazione repo: git clone https://github.com/dario-frongillo/italiancoders-ws-example
  • installazione dipendendenza all interno della folder del progetto: npm install
  • npm run start
  • visita l url http://localhost:3000/ e dai un occhio alla console del browser per leggere i messaggi.

CONCLUSIONE

Come affermato piu volte nell’articolo, se hai bisogno di realtime nelle tue applicazioni web sicuramente la tecnologia di riferimento è il websocket. Come sempre menzioniamo anche le eventuali problematiche dietro ad una certa tecnologia. Nonostante l’ampio supporto del browser, non sono ancora perfettine  IE 8 e 9 ne alcuni altri browser mobili più vecchi. Essi hanno comunque bisogno di fallback se ti interessa un’ampia compatibilità. I piu famosi fallback: SockJS e socket.io.

Concludo. Non fraintendete le belle parole che ho speso sugli websocket come se volessi dire che sono la soluzione ad ogni problema; gli websocket non sostituiscono il classico paradigma request/response su http (come ad es. api Restful). Implementare delle api su websocket non è cosi semplice (dovrai scontrarti a problemi ardui come la concorrenza) e sicuramente ti dovrai appoggiare a protocolli di messaggistica sopra il channel websocket come stomp e il celebre mqtt. Un ultimo fattore da valutare è sicuramente anche il costo lato server su larga scala: ad es per centinaia di migliaia di client potrebbe essere necessario fare un lavoro server speciale per supportare un numero elevato di webSocket connessi simultaneamente. Il mio consiglio è quello di affiancare gli web socket a delle api rest e di utilizzare gli websocket esclusivamente per realtime: esempi di casi di utilizzo chat, notifiche, dati realtime in una griglia, ecc.

 

A proposito di me

Dario Frongillo

Uno degli admin di Italiancoders e dell iniziativa devtalks.
Dario Frongillo è un software engineer e architect, specializzato in Web API, Middleware e Backend in ambito cloud native. Attualmente lavora presso NTT DATA, realtà di consulenza internazionale.
E' membro e contributor in diverse community italiane per developers; Nel 2017 fonda italiancoders.it, una community di blogger italiani che divulga articoli, video e contenuti per developers.

I nostri Partner

Gli articoli più letti

Articoli recenti

Commenti recenti