Utilizzo di WebSocket

Questa pagina fornisce indicazioni e best practice per l'esecuzione di WebSocket o altri servizi di streaming su Cloud Run e per la scrittura di client per questi servizi.

Le applicazioni WebSocket sono supportate su Cloud Run senza bisogno di ulteriori configurazioni. Tuttavia, i flussi WebSocket sono richieste HTTP ancora soggette al timeout della richiesta configurato per il servizio Cloud Run, quindi devi assicurarti che questa impostazione funzioni correttamente per l'utilizzo di WebSocket, ad esempio implementando le riconnessioni nei client.

Anche se utilizzi l'affinità sessione su Cloud Run, che offre l'affinità migliore possibile, le richieste WebSockets potrebbero comunque arrivare a istanze diverse, a causa del bilanciamento del carico integrato. Per risolvere questo problema, devi sincronizzare i dati tra le istanze.

Tieni presente che i WebSocket su Cloud Run sono supportati anche se utilizzi Cloud Load Balancing.

Deployment di un servizio WebSocket di esempio

Utilizza Cloud Shell per eseguire rapidamente il deployment di un servizio di lavagna di esempio che utilizza WebSocket con Cloud Run: Esegui il deployment di un esempio

In alternativa, se vuoi eseguire manualmente il deployment di quel servizio di lavagna di esempio:

  1. Clona il repository Socket.IO in locale utilizzando lo strumento a riga di comando git:

    git clone https://github.com/socketio/socket.io.git
    
  2. Vai alla directory di esempio:

    cd socket.io/examples/whiteboard/
    
  3. Esegui il deployment di un nuovo servizio Cloud Run creando il servizio dal codice sorgente utilizzando Google Cloud CLI:

    gcloud run deploy whiteboard --allow-unauthenticated --source=.
    
  4. Dopo il deployment del servizio, apri due schede separate del browser e vai all'URL del servizio. Tutto ciò che disegni in una scheda deve propagarsi nell'altra scheda (e viceversa) poiché i client sono connessi alla stessa istanza tramite WebSocket.

Tutorial completo di esempio della chat WebSocket

Se vuoi una procedura dettagliata completa per il codice, puoi trovare altri esempi di codice nell'argomento Tutorial: creazione di un servizio WebSocket Chat per Cloud Run.

Best practice

La parte più difficile della creazione di servizi WebSocket in Cloud Run è la sincronizzazione dei dati tra più istanze Cloud Run. Ciò è difficile a causa della scalabilità automatica e della natura stateless delle istanze, nonché per i limiti di contemporaneità e timeout delle richieste.

Gestione dei timeout delle richieste e delle connessioni dei client

Le richieste WebSocket vengono trattate come richieste HTTP a lunga esecuzione in Cloud Run. Sono soggette a timeout delle richieste (attualmente fino a 60 minuti, mentre il valore predefinito è 5 minuti), anche se il server delle applicazioni non applica alcun timeout.

Di conseguenza, se il client mantiene la connessione aperta più a lungo del timeout richiesto configurato per il servizio Cloud Run, il client verrà disconnesso al timeout della richiesta.

Di conseguenza, i client WebSocket che si connettono a Cloud Run devono gestire la riconnessione al server in caso di timeout della richiesta o di disconnessione del server. Puoi ottenere questo risultato nei client basati su browser utilizzando librerie come reconnecting-websocket o gestendo eventi "disconnect" se utilizzi la libreria SocketIO.

Fatturazione sostenuta durante l'utilizzo di WebSocket

Un'istanza Cloud Run con qualsiasi connessione WebSocket aperta viene considerata attiva, quindi la CPU viene allocata e fatturata.

Massimizzazione della contemporaneità

I servizi WebSocket sono in genere progettati per gestire molte connessioni contemporaneamente. Poiché Cloud Run supporta le connessioni simultanee (fino a 1000 per container), Google consiglia di aumentare l'impostazione di contemporaneità massima per il container impostando un valore superiore a quello predefinito se il servizio è in grado di gestire il carico con determinate risorse.

Informazioni sulle sessioni fisse (affinità sessione)

Poiché le connessioni WebSockets sono stateful, il client rimarrà connesso allo stesso container su Cloud Run per tutta la durata della connessione. Questo offre naturalmente una sessione stickiness nel contesto di una singola connessione WebSocket.

Per più connessioni WebSocket e successive, puoi configurare il servizio Cloud Run in modo da utilizzare l'affinità sessione, ma questa offre un'affinità best effort, quindi le richieste WebSockets potrebbero comunque arrivare a essere in istanze diverse. I client che si connettono al tuo servizio Cloud Run potrebbero finire per essere serviti da istanze diverse che non coordinano né condividono i dati.

Per mitigare il problema, devi utilizzare un'archiviazione dati esterna per sincronizzare lo stato tra le istanze Cloud Run, come spiegato nella sezione successiva.

Sincronizzazione dei dati tra istanze

Devi sincronizzare i dati per assicurarti che i client che si connettono a un servizio Cloud Run ricevano gli stessi dati dalla connessione WebSocket.

Ad esempio, supponi di creare un servizio di chat room utilizzando WebSocket e di impostare la massima contemporaneità su 1000. Se più di 1000 utenti si connettono contemporaneamente a questo servizio, verranno gestiti da istanze diverse e, di conseguenza, non potranno visualizzare gli stessi messaggi nella chatroom.

Per sincronizzare i dati tra le istanze Cloud Run, ad esempio per la ricezione dei messaggi pubblicati in una chat room da tutte le istanze, è necessario un sistema di archiviazione dati esterno, ad esempio un database o una coda di messaggi.

Se utilizzi un database esterno come Cloud SQL, puoi inviare messaggi al database ed eseguire il polling dal database periodicamente. Tuttavia, tieni presente che le istanze di Cloud Run non hanno CPU quando il container non gestisce alcuna richiesta. Se il tuo servizio gestisce principalmente le richieste WebSocket, al container la CPU sarà allocata finché è presente almeno un client collegato.

Le code di messaggi funzionano meglio per sincronizzare i dati tra i container Cloud Run in tempo reale, poiché le code di messaggi esterne non possono indirizzare ogni istanza per eseguire il "push" dei dati. I servizi devono eseguire il "pull" di nuovi messaggi dalla coda di messaggi stabilendo una connessione alla coda.

Google consiglia di utilizzare sistemi esterni per le code di messaggi, come Redis Pub/Sub (Memorystore) o aggiornamenti in tempo reale di Firestore, che possono fornire aggiornamenti a tutte le istanze tramite connessioni avviate dall'istanza di container.

Utilizzo di Redis Pub/Sub

Architettura del servizio chatroom WebSockets

Puoi utilizzare il meccanismo Redis Pub/Sub creando un'istanza Redis da Memorystore. Se utilizzi la libreria Socket.IO per WebSocket, puoi utilizzare il relativo adattatore Redis.

In questa architettura basata su Redis, ogni istanza di Cloud Run stabilisce una connessione a lunga esecuzione al canale Redis contenente i messaggi ricevuti (utilizzando il comando SUBSCRIBE). Una volta che le istanze di container ricevono un nuovo messaggio sul canale, possono inviarlo ai client tramite WebSocket in tempo reale.

Allo stesso modo, quando un client emette un messaggio utilizzando WebSocket, l'istanza che lo riceve lo pubblica nel canale Redis (utilizzando il comando PUBLISH) e le altre istanze sottoscritte a questo canale riceveranno questo messaggio.

Se vuoi una procedura dettagliata completa per il codice, puoi trovare altri esempi di codice nell'argomento Tutorial: creazione di un servizio WebSocket Chat per Cloud Run.