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 richiedere configurazioni aggiuntive. Tuttavia, gli stream WebSocket sono richieste HTTP, che sono comunque soggette al timeout della richiesta configurato per il servizio Cloud Run, pertanto devi eseguire le seguenti operazioni:

  • Aumenta il periodo di timeout della richiesta alla durata massima per cui vuoi mantenere aperto lo stream WebSocket, ad esempio 60 minuti.
  • Assicurati che i client siano in grado di riconnettersi.
  • Valuta la possibilità di utilizzare l'affinità sessione per consentire ai client di riconnettersi il più possibile alla stessa istanza.
  • Non attivare HTTP/2 end-to-end.

Sebbene l'affinità sessione su Cloud Run fornisca un'affinità di tipo best effort, le nuove richieste WebSocket potrebbero comunque connettersi 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.

Eseguire il 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 il deployment manualmente di questo servizio di lavagna di esempio:

  1. Clona il repository Socket.IO localmente 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 aver eseguito il deployment del servizio, apri due schede del browser separate e vai all'URL del servizio. Tutto ciò che disegni in una scheda deve essere propagato all'altra scheda (e viceversa), poiché i client sono connessi alla stessa istanza tramite WebSocket.

Tutorial completo di esempio di chat WebSocket

Se vuoi una procedura dettagliata completa del codice, sono disponibili altri esempi di codice in argomento Creazione di un servizio di chat WebSocket per il tutorial di Cloud Run.

Best practice

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

Gestire i timeout delle richieste e le riconnessioni 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 e per impostazione predefinita 5 minuti) anche se il server delle applicazioni non applica alcun timeout.

Di conseguenza, se il client mantiene la connessione aperta per un periodo superiore al timeout richiesto configurato per il servizio Cloud Run, il client verrà disconnesso quando la richiesta raggiunge il timeout.

Pertanto, i client WebSocket che si connettono a Cloud Run devono gestire la riconnessione al server se la richiesta raggiunge il timeout o se il server si disconnette. Puoi farlo nei client basati su browser utilizzando librerie come reconnecting-websocket o gestendo gli eventi "disconnect" se utilizzi la libreria SocketIO.

Fatturazione sostenuta quando si utilizzano i WebSocket

Un'istanza di Cloud Run con qualsiasi connessione WebSocket aperta è considerata attiva, quindi la CPU viene allocata e il servizio viene fatturato in base all'istanza.

Massimizzare la concorrenza

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 concorrenza massima per il container a un valore superiore a quello predefinito se il servizio è in grado di gestire il carico con le risorse specificate.

Informazioni sulle sessioni sticky (affinità sessione)

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

Per più connessioni WebSocket successive, puoi configurare il servizio Cloud Run in modo che utilizzi l'affinità sessione, ma questa fornisce un'affinità di tipo best effort, quindi le richieste WebSocket potrebbero comunque finire in istanze diverse. I client che si connettono al servizio Cloud Run potrebbero essere gestiti da istanze diverse che non coordinano o condividono i dati.

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

Sincronizzare i dati tra le 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.

Supponiamo, ad esempio, di creare un servizio di chatroom utilizzando WebSocket e di impostare la concorrenza massima su 1,000. Se più di 1,000 utenti si connettono a questo servizio contemporaneamente, verranno gestiti da istanze diverse e, di conseguenza, non potranno vedere gli stessi messaggi nella chatroom.

Per sincronizzare i dati tra le istanze di Cloud Run, ad esempio per ricevere i messaggi pubblicati in una chatroom da tutte le istanze, devi utilizzare un sistema di archiviazione dati esterno, come 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 richieste WebSocket, al container verrà allocata la CPU finché almeno un client è connesso.

Le code di messaggi funzionano meglio per sincronizzare i dati tra i container Cloud Run in tempo reale, perché le code di messaggi esterne non possono indirizzare ogni istanza per "inviare" i dati. I tuoi servizi devono "estrarre" i nuovi messaggi dalla coda di messaggi stabilendo una connessione alla coda di messaggi.

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

Utilizzare Redis Pub/Sub

Architettura del servizio di chat WebSocket

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 che contiene i messaggi ricevuti (utilizzando il SUBSCRIBE comando). Una volta che le istanze del container ricevono un nuovo messaggio sul canale, possono inviarlo ai client tramite WebSocket in tempo reale.

Allo stesso modo, quando un client invia un messaggio utilizzando WebSocket, l' istanza che riceve il messaggio lo pubblica sul canale Redis (utilizzando il comando PUBLISH) e le altre istanze che hanno eseguito la sottoscrizione a questo canale riceveranno il messaggio.

Se vuoi una procedura dettagliata completa del codice, sono disponibili altri esempi di codice in argomento Creazione di un servizio di chat WebSocket per il tutorial di Cloud Run.