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.

No Cloud Run, a afinidade da sessão não está disponível. Portanto, as solicitações de WebSockets podem acabar em uma instância de contêiner diferente, devido ao balanceamento de carga integrado. Você precisa sincronizar dados entre as instâncias de contêiner para resolver esse problema.

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 ferramenta de linha de comando gcloud:

    gcloud beta 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 do contêiner nos WebSockets.

Tutorial completo do exemplo de chat do WebSockets

Se você quiser uma explicação completa sobre o código, há outras amostras 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 de contêiner do Cloud Run. Isso é difícil devido ao escalonamento automático e à natureza sem estado das instâncias de contêiner 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 tenha pelo menos uma conexão WebSocket aberta é considerada ativa e, portanto, é 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.

No entanto, como o Cloud Run escalona automaticamente as instâncias de contêiner e faz o balanceamento de carga de todas as solicitações entre as instâncias de contêiner disponíveis, ele não oferece permanência de sessão entre solicitações. Portanto, as solicitações subsequentes do mesmo cliente terão o balanceamento de carga aleatório entre as instâncias de contêiner e poderão abrir WebSockets para uma instância de contêiner diferente.

Devido ao balanceamento de carga entre instâncias de contêiner, os clientes que se conectam ao seu serviço do Cloud Run podem acabar sendo atendidos por instâncias de contêiner diferentes que não coordenam ou compartilham dados. Para atenuar isso, é necessário usar um armazenamento 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 as instâncias de contêiner

Devido à natureza sem estado e ao escalonamento automático das instâncias de contêiner do Cloud Run, os clientes que se conectam a um serviço do Cloud Run podem não receber os mesmos dados da conexão de WebSockets.

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

Para sincronizar dados entre as instâncias de contêiner 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 funcionará 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 do contêiner 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á outras amostras de código disponíveis no tópico Como criar um serviço de WebSocket Chat para o Cloud Run.