Usar WebSockets

En esta página, se proporcionan asesoramiento y prácticas recomendadas para ejecutar WebSockets y otros servicios de transmisión en Cloud Run y escribir clientes para esos servicios.

Las aplicaciones WebSockets son compatibles con Cloud Run sin necesidad de una configuración adicional. Sin embargo, las transmisiones de WebSockets son solicitudes HTTP que están sujetas al tiempo de espera de la solicitud configurado para tu servicio de Cloud Run, por lo que debes asegurarte de que esta configuración funcione bien con tu uso de WebSockets, como la implementación de las reconexiones de tus clientes.

Incluso si usas la afinidad de sesión en Cloud Run, que proporciona afinidad de mejor esfuerzo, las solicitudes de WebSockets podrían terminar en diferentes instancias debido al balanceo de cargas integrado. Para resolver este problema, debes sincronizar datos entre instancias.

Ten en cuenta que los WebSockets en Cloud Run también son compatibles si usas Cloud Load Balancing.

Implementa un servicio de WebSockets de muestra

Usa Cloud Shell para reproducir rápidamente un servicio de pizarra de muestra que usa WebSockets con Cloud Run: Implementa una muestra.

Si deseas implementar ese servicio de pizarra de forma manual, haz lo siguiente:

  1. Clona el repositorio de Socket.IO de manera local mediante la herramienta de línea de comandos de git:

    git clone https://github.com/socketio/socket.io.git
    
  2. Navega al directorio de muestra:

    cd socket.io/examples/whiteboard/
    
  3. Implementa un servicio de Cloud Run nuevo mediante la compilación del servicio desde el código fuente mediante Google Cloud CLI:

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

Instructivo completo para el chat de muestra de WebSockets

Si deseas una explicación del código completa, hay muestras de código adicionales disponibles en el tema Instructivo sobre compilación de un servicio de chat basado en WebSocket para Cloud Run.

Prácticas recomendadas

La parte más difícil de crear servicios de WebSockets en Cloud Run es la sincronización de datos entre varias instancias de Cloud Run. Esto es difícil debido al ajuste de escala automático y sin estado de las instancias, y debido a los límites de simultaneidad y tiempos de espera de solicitudes.

Controla los tiempos de espera de las solicitudes y las reconexiones del cliente

Las solicitudes de WebSockets se tratan como solicitudes HTTP de larga duración en Cloud Run. Están sujetas a tiempos de espera de solicitudes (actualmente, hasta 60 minutos y se configuran de forma predeterminada en 5 minutos) incluso si el servidor de tu aplicación no impone ningún tiempo de espera.

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

Por lo tanto, los clientes de WebSockets que se conectan a Cloud Run deben controlar la reconexión al servidor si se agota el tiempo de espera de la solicitud o se desconecta el servidor. Puedes lograr esto en clientes basados en el navegador mediante bibliotecas como reconnecting-websocket o mediante el control de eventos de “desconexión” si usas la biblioteca SocketIO.

Facturación generada cuando se usan WebSockets

Una instancia de Cloud Run que tiene cualquier conexión de WebSocket abierta se considera activa, por lo que se asigna y factura la CPU.

Maximiza la simultaneidad

Por lo general, los servicios de WebSockets están diseñadas para manejar muchas conexiones en simultáneo. Debido a que Cloud Run admite conexiones simultáneas (hasta 1000 por contenedor), Google recomienda que aumentes la configuración de simultaneidad máxima para tu contenedor a un valor más alto que el predeterminado si tu servicio puede controlar la carga con recursos determinados.

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

Debido a que las conexiones de WebSockets son con estado, el cliente permanecerá conectado al mismo contenedor en Cloud Run durante toda la vida útil de la conexión. Esto, de manera natural, ofrece fijación de sesión dentro del contexto de una sola conexión de WebSocket.

Para varias conexiones de WebSockets y posteriores, puedes configurar tu servicio de Cloud Run y usar la afinidad de sesión, pero esto proporciona una afinidad de mejor esfuerzo, por lo que las solicitudes de WebSockets podrían llegar a instancias diferentes. Es posible que los clientes que se conectan a tu servicio de Cloud Run reciban servicios de diferentes instancias que no coordinan ni comparten datos.

A fin de mitigar esto, debes usar un almacenamiento de datos externo para sincronizar el estado entre las instancias de Cloud Run, que se explica en la siguiente sección.

Sincroniza datos entre instancias

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

Por ejemplo, si compilas un servicio de sala de chat mediante WebSockets y estableces la configuración de simultaneidad máxima en 1000, cuando más de 1000 usuarios se conecten a este servicio al mismo tiempo, recibirán servicio de diferentes instancias y, por lo tanto, no podrán ver los mismos mensajes en la sala de chat.

Para sincronizar datos entre tus instancias de Cloud Run, como recibir los mensajes publicados en una sala de chat de todas las instancias, necesitarás 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 sondear la base de datos de forma periódica. Sin embargo, ten en cuenta que las instancias de Cloud Run no tienen CPU cuando el contenedor no ninguna solicitud. Si tu servicio controla principalmente las solicitudes de WebSockets, el contenedor tendrá CPU asignada, siempre que haya al menos un cliente conectado a él.

El uso de colas de mensajes funcionará 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” mensajes nuevos de la cola de mensajes con estableciendo una conexión con esa cola.

Google recomienda que uses sistemas externos de cola de mensajes, como Redis Pub/Sub (Memorystore) o Actualizaciones en tiempo real de Firestore que pueden entregar actualizaciones a todas las instancias mediante conexiones iniciadas por la instancia de contenedor.

Usa Pub/Sub de Redis

Arquitectura del servicio de sala de chat de WebSockets

Puedes usar el mecanismo de Pub/Sub de Redis, crea 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 SUBSCRIBE). Una vez que las instancias de contenedor reciban un mensaje nuevo en el canal, pueden enviarlos a sus clientes a través de WebSockets en tiempo real.

De manera similar, cuando un cliente emite un mensaje mediante WebSockets, la instancia que recibe el mensaje publica el mensaje en el canal de Redis (mediante el PUBLISH) y otras instancias que estén suscritas a este canal recibirán este mensaje.

Si deseas una explicación del código completa, hay muestras de código adicionales disponibles en el tema Instructivo sobre compilación de un servicio de chat basado en WebSocket para Cloud Run.