Esta página fornece orientações e práticas recomendadas para executar WebSockets ou outros serviços de streaming no Cloud Run e escrever clientes para esses serviços.
As aplicações WebSockets são suportadas no Cloud Run sem necessidade de configuração adicional. No entanto, as streams WebSocket são pedidos HTTP, que ainda estão sujeitos ao tempo limite do pedido configurado para o seu serviço do Cloud Run, pelo que tem de fazer o seguinte:
- Aumente o período de tempo limite do pedido para a duração máxima que quer manter a stream WebSockets aberta, por exemplo, 60 minutos.
- Certifique-se de que os seus clientes conseguem restabelecer a ligação.
- Pondere usar a afinidade de sessão para que os clientes se voltem a ligar o máximo possível à mesma instância.
- Não ative o HTTP/2 ponto a ponto.
Embora a afinidade de sessão no Cloud Run ofereça a melhor afinidade possível, os novos pedidos de WebSockets podem potencialmente ligar-se a instâncias diferentes devido ao equilíbrio de carga incorporado. Para resolver este problema, tem de sincronizar os dados entre instâncias.
Tenha em atenção que os WebSockets no Cloud Run também são suportados se estiver a usar o Cloud Load Balancing.
Implementar um serviço WebSockets de exemplo
Use o Cloud Shell para implementar rapidamente um serviço de quadro branco de exemplo que usa WebSockets com o Cloud Run: Implemente um exemplo
Em alternativa, se quiser implementar esse serviço de quadro branco de exemplo manualmente:
Clone o repositório Socket.IO localmente através da ferramenta de linhas de comando git:
git clone https://github.com/socketio/socket.io.git
Navegue para o diretório de exemplo:
cd socket.io/examples/whiteboard/
Implemente um novo serviço do Cloud Run criando o serviço a partir do código fonte através da CLI do Google Cloud:
gcloud run deploy whiteboard --allow-unauthenticated --source=.
Após a implementação do serviço, abra dois separadores do navegador separados e navegue para o URL do serviço. Tudo o que desenhar num separador deve propagar-se para o outro separador (e vice-versa), uma vez que os clientes estão ligados à mesma instância através de WebSockets.
Tutorial completo do exemplo de chat com WebSockets
Se quiser uma explicação detalhada do código, estão disponíveis exemplos de código adicionais no tópico Criar um serviço de chat WebSocket para o tutorial do Cloud Run.
Práticas recomendadas
A parte mais difícil da criação de serviços WebSockets no Cloud Run é a sincronização de dados entre várias instâncias do Cloud Run. Isto é difícil devido à escalabilidade automática e à natureza sem estado das instâncias, bem como aos limites de concorrência e tempos limite de pedidos.
Processar limites de tempo de pedidos e novas ligações de clientes
Os pedidos WebSockets são tratados como pedidos HTTP de execução prolongada no Cloud Run. Estão sujeitos a tempos limite de pedidos (atualmente, até 60 minutos e, por predefinição, 5 minutos), mesmo que o servidor da sua aplicação não aplique tempos limite.
Assim, se o cliente mantiver a ligação aberta durante mais tempo do que o limite de tempo limite necessário configurado para o serviço Cloud Run, o cliente é desligado quando o pedido atinge o limite de tempo.
Por conseguinte, os clientes WebSocket que se ligam ao Cloud Run devem processar a nova ligação ao servidor se o pedido exceder o tempo limite ou o servidor se desligar. Pode conseguir isto em clientes baseados no navegador através de bibliotecas como reconnecting-websocket ou através do processamento de eventos "disconnect" se estiver a usar a biblioteca SocketIO.
Faturação incorrida quando usa WebSockets
Uma instância do Cloud Run que tenha qualquer ligação WebSocket aberta é considerada ativa, pelo que a CPU é alocada e o serviço é faturado como faturação baseada em instâncias.
Maximizar a simultaneidade
Normalmente, os serviços WebSockets são concebidos para processar muitas ligações em simultâneo. Uma vez que o Cloud Run suporta ligações simultâneas (até 1000 por contentor), a Google recomenda que aumente a definição de simultaneidade máxima do seu contentor para um valor superior ao predefinido se o seu serviço conseguir processar a carga com os recursos fornecidos.
Acerca das sessões persistentes (afinidade de sessão)
Uma vez que as ligações WebSocket têm estado, o cliente permanece ligado ao mesmo contentor no Cloud Run durante todo o ciclo de vida da ligação. Isto oferece naturalmente uma persistência da sessão no contexto de uma única ligação WebSocket.
Para várias ligações WebSockets subsequentes, pode configurar o seu serviço do Cloud Run para usar a afinidade de sessão, mas isto oferece uma afinidade de melhor esforço, pelo que os pedidos WebSockets podem potencialmente acabar em instâncias diferentes. Os clientes que se ligam ao seu serviço do Cloud Run podem acabar por ser atendidos por instâncias diferentes que não coordenam nem partilham dados.
Para mitigar esta situação, tem de usar um armazenamento de dados externo para sincronizar o estado entre as instâncias do Cloud Run, o que é explicado na secção seguinte.
Sincronizar dados entre instâncias
Tem de sincronizar os dados para garantir que os clientes que se ligam a um serviço do Cloud Run recebem os mesmos dados da ligação WebSockets.
Por exemplo, suponhamos que está a criar um serviço de sala de chat com WebSockets e define a sua definição de concorrência máxima para 1000
. Se mais de 1000
utilizadores estabelecerem ligação a este serviço em simultâneo, são atendidos por instâncias diferentes e, por isso, não conseguem ver as mesmas mensagens na sala de chat.
Para sincronizar dados entre as suas instâncias do Cloud Run, como receber as mensagens publicadas numa sala de chat de todas as instâncias, precisa de um sistema de armazenamento de dados externo, como uma base de dados ou uma fila de mensagens.
Se usar uma base de dados externa, como o Cloud SQL, pode enviar mensagens para a base de dados e fazer sondagens periódicas à base de dados. No entanto, tenha em atenção que as instâncias do Cloud Run não têm CPU quando o contentor não está a processar pedidos. Se o seu serviço processar principalmente pedidos de WebSockets, o contentor tem CPU alocada desde que haja, pelo menos, um cliente ligado ao mesmo.
As filas de mensagens funcionam melhor para sincronizar dados entre contentores do Cloud Run em tempo real, porque as filas de mensagens externas não podem direcionar cada instância para "enviar" dados. Os seus serviços têm de "extrair" novas mensagens da fila de mensagens estabelecendo uma ligação à fila de mensagens.
A Google recomenda que use sistemas de filas de mensagens externos, como o Redis Pub/Sub (Memorystore) ou as atualizações em tempo real do Firestore, que podem enviar atualizações para todas as instâncias através de ligações iniciadas pela instância do contentor.
Usar o Redis Pub/Sub
Pode usar o mecanismo Redis Pub/Sub criando uma instância do Redis a partir do Memorystore. Se estiver a usar a biblioteca Socket.IO para WebSockets, pode usar o respetivo adaptador redis.
Nesta arquitetura baseada no Redis, cada instância do Cloud Run estabelece uma ligação de longa duração ao canal do Redis que contém as mensagens recebidas (através do comando SUBSCRIBE
). Assim que as instâncias do contentor recebem uma nova mensagem no canal, podem enviá-la aos respetivos clientes através de WebSockets em tempo real.
Da mesma forma, quando um cliente emite uma mensagem através de WebSockets, a instância que recebe a mensagem publica a mensagem no canal Redis (através do comando PUBLISH
), e outras instâncias subscritas neste canal recebem esta mensagem.
Se quiser uma explicação detalhada do código, estão disponíveis exemplos de código adicionais no tópico Criar um serviço de chat WebSocket para o tutorial do Cloud Run.