Como usar WebSockets

Nesta página, você encontra orientações e práticas recomendadas para a execução de WebSockets ou outros serviços de streaming no Cloud Run e a gravação de clientes para esses serviços.

Os WebSockets são compatíveis com o Cloud Run sem a necessidade de configuração adicional. No entanto, os streams de WebSockets são solicitações HTTP ainda sujeitas ao tempo limite da solicitação configurado para o serviço do Cloud Run. Por isso, é preciso garantir que essa configuração funcione bem para o uso de WebSockets, como a implementação de reconexões nos seus clientes.

Mesmo se você usar a afinidade da sessão no Cloud Run, que oferece uma afinidade de melhor esforço, as solicitações do WebSockets ainda poderão acabar em instâncias de contêiner diferentes devido ao balanceamento de carga integrado. Para resolver esse problema, é necessário sincronizar dados entre instâncias.

Os WebSockets no Cloud Run também serão aceitos se você estiver usando o Cloud Load Balancing.

Como implantar um serviço WebSockets de amostra

Use o Cloud Shell para implantar rapidamente um serviço de quadro branco de amostra que usa WebSockets com o Cloud Run: Implantar uma amostra

Ou, caso queira implantar esse serviço de quadro branco de amostra manualmente:

  1. Clone o repositório Socket.IO localmente usando a ferramenta de linha de comando git:

    git clone https://github.com/socketio/socket.io.git
    
  2. Navegue até o diretório da amostra:

    cd socket.io/examples/whiteboard/
    
  3. Implante um novo serviço do Cloud Run criando o serviço a partir do código-fonte usando a CLI do Google Cloud

    gcloud run deploy whiteboard --allow-unauthenticated --source=.
    
  4. Depois que o serviço for implantado, abra duas guias separadas do navegador e acesse o URL do serviço. Tudo o que você desenhar em uma guia será propagado para a outra guia (e vice-versa), já que os clientes estão conectados à mesma instância nos WebSockets.

Tutorial completo do exemplo de chat do WebSockets

Se você quiser uma explicação completa sobre o código, há outros exemplos de código disponíveis no tópico Como criar um serviço de WebSocket Chat para o Cloud Run.

Práticas recomendadas

A parte mais difícil da criação de serviços WebSockets no Cloud Run é sincronizar dados entre várias instâncias do Cloud Run. Isso é difícil devido ao escalonamento automático e à natureza sem estado das instâncias e também aos limites de simultaneidade e tempos limites de solicitação.

Como lidar com tempos limites de solicitação e reconexões do cliente

As solicitações de WebSockets são tratadas como solicitações HTTP de longa duração no Cloud Run. Elas estão sujeitos a tempos limite de solicitação (atualmente até 60 minutos e são definidos como 5 minutos), mesmo que o servidor do aplicativo não imponha nenhum tempo limite.

Da mesma forma, se o cliente mantiver a conexão aberta por mais tempo que o tempo limite necessário configurado para o serviço do Cloud Run, o cliente será desconectado quando a solicitação atingir o tempo limite.

Portanto, os clientes de WebSockets que se conectam ao Cloud Run devem se reconectar ao servidor se a solicitação expirar ou o servidor se desconectar. É possível fazer isso em clientes baseados em navegador usando bibliotecas como reconnecting-websocket ou lidar com eventos de "desconexão" se você estiver usando a biblioteca SocketO.

Faturamento com o uso de WebSockets

Uma instância do Cloud Run que tem qualquer conexão aberta do WebSocket é considerada ativa, então a CPU é alocada e faturada.

Como maximizar a simultaneidade

Os serviços WebSockets normalmente são projetados para lidar com muitas conexões simultaneamente. Como o Cloud Run é compatível com conexões simultâneas (até 1000 por contêiner), o Google recomenda que você aumente a configuração máxima de simultaneidade do seu contêiner para um valor maior do que o padrão se o serviço for capaz de lidar com a carga com determinados recursos.

Sobre as sessões fixas (afinidade da sessão)

Como as conexões de WebSockets têm estado, o cliente permanecerá conectado ao mesmo contêiner no Cloud Run durante a vida útil da conexão. Isso oferece naturalmente uma permanência de sessão no contexto de uma única conexão WebSocket.

Para conexões WebSockets múltiplas e subsequentes, é possível configurar o serviço do Cloud Run para usar a afinidade da sessão, mas isso fornece uma afinidade de melhor esforço. Por isso, as solicitações do WebSockets ainda podem acabar em instâncias diferentes. Os clientes que se conectam ao serviço do Cloud Run podem acabar sendo atendidos por instâncias diferentes que não se coordenam entre si ou compartilham dados.

Para atenuar isso, é necessário usar um repositório de dados externo para sincronizar o estado entre as instâncias do Cloud Run, o que será explicado na próxima seção.

Como sincronizar dados entre instâncias

Você precisa sincronizar dados para garantir que os clientes que se conectam a um serviço do Cloud Run recebam os mesmos dados da conexão do WebSockets.

Por exemplo, se você estiver criando um aplicativo de chat usando o WebSockets e definir a configuração de simultaneidade máxima como 1000, se mais de 1000 usuários se conectarem ao mesmo tempo, eles serão atendidos por diferentes instâncias. Por isso, não poderão ver as mesmas mensagens na sala do chat.

Para sincronizar dados entre as instâncias do Cloud Run, como receber as mensagens postadas em uma sala de bate-papo em todas as instâncias, você precisa de um sistema de armazenamento de dados externo, como um banco de dados ou uma fila de mensagens.

Se você usar um banco de dados externo, como o Cloud SQL, poderá enviar mensagens para o banco de dados e pesquisar o banco de dados periodicamente. No entanto, observe que as instâncias do Cloud Run não têm CPU quando o contêiner não está processando solicitações. Se o serviço processar principalmente solicitações de WebSockets, o contêiner terá uma CPU alocada, desde que haja pelo menos um cliente conectado a ele.

O uso de filas de mensagens funciona melhor para sincronizar dados entre os contêineres do Cloud Run em tempo real, porque as filas de mensagens externas não podem atender a cada instância do contêiner para enviar dados por "push". Seus aplicativos precisam "extrair" novas mensagens da fila de mensagens, estabelecendo uma conexão com ela.

O Google recomenda o uso de sistemas de fila de mensagens externas, como o Redis Pub/Sub (Memorystore) ou as atualizações em tempo real do Firestore que podem entregar atualizações para todas as instâncias por conexões iniciadas pela instância do contêiner.

Como usar o Redis Pub/Sub

Arquitetura de serviço de sala de chat de WebSockets

Use o mecanismo do Pub/Sub do Redis criando uma instância do Redis a partir do Memorystore. Se você estiver usando a biblioteca Socket.IO para WebSockets, utilize seu adaptador do redis.

Nesta arquitetura baseada no Redis, cada instância de contêiner do Cloud Run estabelece uma conexão de longa duração com o canal do Redis que contém as mensagens recebidas (usando o comando SUBSCRIBE). Quando as instâncias do contêiner recebem uma nova mensagem no canal, elas podem enviá-la aos clientes em WebSockets em tempo real.

Da mesma forma, quando um cliente emite uma mensagem usando WebSockets, a instância que recebe a mensagem publica a mensagem no canal Redis (usando o comando PUBLISH) e outras instâncias de contêiner inscritas neste canal receberão esta mensagem.

Se você quiser uma explicação completa sobre o código, há outros exemplos de código disponíveis no tópico Como criar um serviço de WebSocket Chat para o Cloud Run.