← Blog
engineeringreal-timewebrtcwebsocketsarchitecture

Il real-time è più difficile di come appare

13 marzo 2026

Il real-time è più difficile di come appare

Il real-time sembra semplice dall'esterno. Apri una WebSocket, spingi eventi, i client si aggiornano. La demo funziona in dieci minuti. Poi la metti in produzione e passi l'anno successivo a capire cosa quella demo nascondeva.

Ho integrato il real-time in più prodotti. Clover e Argan sono piattaforme di messaggistica e conferenza — messaggistica completa via WebSocket, chiamate audio e video, indicatori di presenza, indicatori di digitazione, ricevute di consegna. Poi ho riscritto il motore di chiamate, arrivando a Elderberry costruito su mediasoup. Ecco quello che ho imparato davvero.

Le WebSocket non sono difficili — lo è la gestione dello stato di connessione

L'API WebSocket è semplice. Ti connetti, mandi messaggi, ne ricevi, gestisci le disconnessioni. In un ambiente controllato, funziona esattamente come ti aspetti.

La complessità non sta nell'API. Sta nella macchina a stati che devi costruirci intorno.

Cosa succede quando un client si disconnette a metà sessione? Cosa succede quando si riconnette — rientra in una stanza? Con quale stato? E se il server si riavvia mentre i client sono connessi? E se ci sono due schede del browser aperte e l'utente manda un messaggio da una — l'altra si aggiorna?

Tutti questi casi non si incontrano in sviluppo locale. Tutti accadono in produzione, continuamente, dal primo giorno in cui hai utenti reali. L'implementazione ingenua non ne gestisce nessuno. L'implementazione production è quasi tutta gestione dello stato di connessione, con un sottile strato di messaggistica vera sopra.

La presenza è la parte più difficile

La presenza real-time — "chi è attualmente online" — sembra una funzionalità semplice. È una delle cose più insidiosamente difficili da implementare correttamente.

L'approccio ovvio: utente si connette → segnato come online. Utente si disconnette → segnato come offline. Si rompe subito, per una ragione specifica: un utente non è una connessione. Un utente è una persona che può avere tre tab aperti, un telefono e un client desktop connessi contemporaneamente. Se uno si disconnette, non è offline.

La versione che funziona davvero tiene traccia delle connessioni per utente, non per socket. In Clover e Argan, ogni socket entra in una stanza Socket.IO con chiave uguale all'ID utente alla connessione. Il server mantiene un contatore delle connessioni attive per utente. Un evento di disconnessione decrementa il contatore — e segna l'utente offline solo se il contatore arriva a zero. Finché almeno un dispositivo è ancora connesso, la presenza rimane verde.

L'implementazione è semplice una volta che si vede il modello. Arrivarci richiede di capire che il primitivo che conta è l'utente, non il socket. Quando hai una presenza stabile, hai costruito un piccolo problema di sistemi distribuiti. Il che va bene — vale la pena saperlo in anticipo.

WebRTC: il peer-to-peer è una bugia

La promessa di WebRTC è il peer-to-peer per audio e video — connessioni dirette tra browser, senza server media. In casi semplici è vero. In produzione, per lo più non lo è.

Il primo problema è il traversal NAT. La maggior parte degli utenti è dietro router che bloccano le connessioni in entrata non richieste. Il peer-to-peer richiede server STUN per scoprire gli indirizzi pubblici e server TURN per il relay dei media quando la connessione diretta fallisce. Una frazione significativa dei tentativi di chiamata richiederà il relay TURN — si stima comunemente che il 20–30% delle chiamate reali passi per TURN. Gestire un server TURN è un onere infrastrutturale non banale.

Il secondo problema è la topologia. In una chiamata con più di due partecipanti, il puro peer-to-peer significa che ogni partecipante invia e riceve N-1 stream. Quattro persone in una chiamata significa che ognuna invia tre stream video e ne riceve tre. Non scala. Con sei persone si nota. Con dodici è inutilizzabile.

La soluzione è un Selective Forwarding Unit — un media server che riceve uno stream da ogni partecipante e lo inoltra agli altri. È quello che fornisce mediasoup.

Perché ho riscritto il motore media

Il Clover e Argan originali usavano WebRTC con un server TURN. Funzionava. Ma i prodotti sono self-hosted, venduti a persone che vogliono gestirli in autonomia. E gestire un server TURN in self-hosting è genuinamente scomodo — richiede un IP pubblico, porte UDP specifiche aperte, configurazione separata dall'app principale.

Il carico di supporto era dominato da problemi di qualità delle chiamate riconducibili a una configurazione errata del TURN. Gli utenti non volevano gestire il server TURN. Volevano che le chiamate funzionassero.

Passare a mediasoup ha cambiato l'architettura: invece di connettersi peer-to-peer attraverso il TURN, i client si connettono direttamente al server mediasoup, che gestisce tutto il routing dei media. Nessun server TURN necessario. Deploy più semplice, migliore scalabilità, più controllo sulla qualità.

La riscrittura è stata significativa — l'intero flusso delle chiamate è cambiato. Ma ha risolto il problema reale, che non era tecnico al livello di WebRTC. Era operativo: gli utenti non potevano o non volevano configurare l'infrastruttura che l'architettura originale richiedeva.

Cosa richiede davvero il real-time

Dopo tutto questo, il pattern che vedo è che la complessità del real-time non sta quasi mai nel protocollo. Le WebSocket funzionano. WebRTC funziona. La complessità sta in tutto ciò che gli sta intorno: ciclo di vita della connessione, recovery dello stato, semplicità operativa per chi gestisce l'infrastruttura.

Le cose che direi a qualcuno che inizia un sistema real-time oggi:

  • Costruisci la logica di riconnessione e recovery dello stato da subito. Non è una feature di rifinitura.
  • La presenza basata solo sugli eventi di connessione ti deluderà. Gli heartbeat non sono opzionali.
  • Se hai bisogno di audio/video con più di due partecipanti, ti serve un SFU. Mettilo in conto dall'inizio.
  • La storia del deploy è parte del prodotto. Un'architettura che richiede agli utenti di gestire infrastruttura che non capiscono è un problema di supporto che aspetta solo di manifestarsi.

Il real-time è difficile. Non perché i mattoni siano complicati, ma perché i casi limite sono invisibili in sviluppo e costanti in produzione. Costruisci pensando a loro fin dall'inizio.