Usar WebSockets

En esta página se ofrecen directrices y prácticas recomendadas para ejecutar WebSockets u otros servicios de streaming en Cloud Run, así como para escribir clientes para dichos servicios.

Las aplicaciones WebSockets se admiten en Cloud Run sin necesidad de configuración adicional. Sin embargo, las secuencias de WebSockets son solicitudes HTTP, que siguen estando sujetas al tiempo de espera de las solicitudes configurado para tu servicio de Cloud Run, por lo que debes hacer lo siguiente:

Aunque la afinidad de sesión en Cloud Run proporciona la mejor afinidad posible, las nuevas solicitudes de WebSockets podrían conectarse a instancias diferentes debido al balanceo de carga integrado. Para solucionar este problema, debes sincronizar los datos entre las instancias.

Ten en cuenta que también se admiten WebSockets en Cloud Run si usas Cloud Load Balancing.

Desplegar un servicio de WebSockets de ejemplo

Usa Cloud Shell para desplegar rápidamente un servicio de pizarra de ejemplo que utilice WebSockets con Cloud Run: Desplegar un ejemplo

Si quieres implementar ese servicio de pizarra de ejemplo manualmente, sigue estos pasos:

  1. Clona el repositorio de Socket.IO de forma local con la herramienta de línea de comandos de Git:

    git clone https://github.com/socketio/socket.io.git
    
  2. Ve al directorio de ejemplo:

    cd socket.io/examples/whiteboard/
    
  3. Despliega un nuevo servicio de Cloud Run compilando el servicio a partir del código fuente con Google Cloud CLI:

    gcloud run deploy whiteboard --allow-unauthenticated --source=.
    
  4. Una vez que se haya implementado el servicio, abre dos pestañas del navegador independientes y ve a la URL del servicio. Todo lo que dibujes en una pestaña se propagará a la otra (y viceversa), ya que los clientes están conectados a la misma instancia a través de WebSockets.

Tutorial completo de ejemplo de chat de WebSockets

Si quieres ver una guía completa del código, puedes consultar otros ejemplos de código en el tema Crear un servicio de chat WebSocket para el tutorial de Cloud Run.

Prácticas recomendadas

La parte más difícil de crear servicios WebSockets en Cloud Run es sincronizar los datos entre varias instancias de Cloud Run. Esto es difícil debido al autoescalado y a la naturaleza sin estado de las instancias, así como a los límites de concurrencia y tiempos de espera de las solicitudes.

Gestionar los tiempos de espera de las solicitudes y las reconexiones de los clientes

Las solicitudes de WebSockets se tratan como solicitudes HTTP de larga duración en Cloud Run. Están sujetos a tiempos de espera de las solicitudes (actualmente, hasta 60 minutos y, de forma predeterminada, 5 minutos), aunque tu servidor de aplicaciones no aplique ningún tiempo de espera.

Por lo tanto, si el cliente mantiene la conexión abierta durante más tiempo del tiempo de espera configurado para el servicio de Cloud Run, se desconectará cuando se agote el tiempo de espera de la solicitud.

Por lo tanto, los clientes de WebSockets que se conecten a Cloud Run deben gestionar la reconexión al servidor si se agota el tiempo de espera de la solicitud o si el servidor se desconecta. Puedes hacerlo en clientes basados en navegador usando bibliotecas como reconnecting-websocket o gestionando eventos "disconnect" si usas la biblioteca SocketIO.

Facturación incurrida al usar WebSockets

Una instancia de Cloud Run que tenga alguna conexión WebSocket abierta se considera activa, por lo que se asigna CPU y el servicio se factura como facturación basada en instancias.

Maximizar la simultaneidad

Los servicios WebSockets suelen diseñarse para gestionar muchas conexiones simultáneamente. Como Cloud Run admite conexiones simultáneas (hasta 1000 por contenedor), Google recomienda que aumentes el valor predeterminado del ajuste de simultaneidad máxima de tu contenedor si tu servicio puede gestionar la carga con los recursos disponibles.

Acerca de las sesiones fijas (afinidad de sesión)

Como las conexiones WebSockets tienen estado, el cliente permanecerá conectado al mismo contenedor en Cloud Run durante toda la duración de la conexión. De esta forma, se ofrece una permanencia de sesión en el contexto de una sola conexión WebSocket.

En el caso de varias conexiones WebSocket posteriores, puedes configurar tu servicio de Cloud Run para que use la afinidad de sesión, pero esta opción proporciona una afinidad de mejor esfuerzo, por lo que las solicitudes WebSocket podrían seguir llegando a instancias diferentes. Es posible que los clientes que se conecten a tu servicio de Cloud Run acaben recibiendo asistencia de diferentes instancias que no coordinan ni comparten datos.

Para mitigar este problema, debes usar un almacenamiento de datos externo para sincronizar el estado entre las instancias de Cloud Run, tal como se explica en la siguiente sección.

Sincronizar datos entre instancias

Debes sincronizar los datos para asegurarte de que los clientes que se conecten a un servicio de Cloud Run reciban los mismos datos de la conexión WebSockets.

Por ejemplo, supongamos que estás creando un servicio de sala de chat con WebSockets y que has definido el valor 1000 en el ajuste concurrencia máxima. Si más de 1000 usuarios se conectan a este servicio al mismo tiempo, se les proporcionarán diferentes instancias y, por lo tanto, no podrán ver los mismos mensajes en la sala de chat.

Para sincronizar los datos entre tus instancias de Cloud Run, como recibir los mensajes publicados en una sala de chat desde todas las instancias, necesitas un sistema de almacenamiento de datos externo, como una base de datos o una cola de mensajes.

Si usas una base de datos externa, como Cloud SQL, puedes enviar mensajes a la base de datos y sondearla periódicamente. Sin embargo, ten en cuenta que las instancias de Cloud Run no tienen CPU cuando el contenedor no gestiona ninguna solicitud. Si tu servicio gestiona principalmente solicitudes de WebSockets, el contenedor tendrá CPU asignada mientras haya al menos un cliente conectado a él.

Las colas de mensajes funcionan mejor para sincronizar datos entre contenedores de Cloud Run en tiempo real, ya que las colas de mensajes externas no pueden dirigirse a cada instancia para "enviar" datos. Tus servicios deben "extraer" los mensajes nuevos de la cola de mensajes estableciendo una conexión con ella.

Google recomienda usar sistemas de colas de mensajes externos, como Redis Pub/Sub (Memorystore) o actualizaciones en tiempo real de Firestore, que pueden enviar actualizaciones a todas las instancias a través de conexiones iniciadas por la instancia del contenedor.

Usar Pub/Sub de Redis

Arquitectura del servicio de sala de chat de WebSockets

Puedes usar el mecanismo de publicación/suscripción de Redis creando una instancia de Redis desde Memorystore. Si usas la biblioteca Socket.IO para WebSockets, puedes usar su adaptador de Redis.

En esta arquitectura basada en Redis, cada instancia de Cloud Run establece una conexión de larga duración con el canal de Redis que contiene los mensajes recibidos (mediante el comando SUBSCRIBE). Una vez que las instancias del contenedor reciban un mensaje nuevo en el canal, podrán enviarlo a sus clientes a través de WebSockets en tiempo real.

Del mismo modo, cuando un cliente emite un mensaje mediante WebSockets, la instancia que recibe el mensaje lo publica en el canal de Redis (mediante el comando PUBLISH) y las demás instancias suscritas a este canal recibirán el mensaje.

Si quieres ver una guía completa del código, puedes consultar otros ejemplos de código en el tema Crear un servicio de chat WebSocket para el tutorial de Cloud Run.