DevGuideTK
← Trilha Backend

Tempo real (WebSocket e SSE)

Formas de o servidor enviar dados ao cliente sem esperar um pedido, e o custo de manter conexões abertas.

As decisões de tempo real são cinco: qual forma usar, como levar o evento à instância certa, como não perder eventos, como proteger as conexões e como dimensionar a infraestrutura para conexões longas.

Comparação

Consultas periódicasSSEWebSocket
DireçãoCliente perguntaServidor enviaOs dois enviam
ProtocoloHTTP comumHTTP comum, resposta longaProtocolo próprio, depois de um pedido HTTP
AtrasoAté um intervalo inteiroImediatoImediato
Reconexão e retomadaNão se aplicaEmbutidas no navegadorPor conta da aplicação
Dados bináriosSimSó textoSim
Proxies e balanceadoresSem cuidadosCuidado com buffer e tempo de ociosidadePrecisam suportar o protocolo; tempo de ociosidade
AutenticaçãoA de sempreA de sempre (cookie; o EventSource não envia cabeçalhos próprios)Na abertura, por cookie, token na primeira mensagem ou na URL
ComplexidadeMínimaBaixaMédia a alta

Levar o evento à instância certa

AbordagemComo funcionaQuando usar
Publicação e assinatura do RedisToda instância assina um canal; quem gera o evento publica nele; cada instância repassa aos clientes conectados a elaO padrão para a maioria dos sistemas
Fila ou log de eventosAs instâncias consomem um tópico do Kafka ou do RabbitMQQuando os eventos já trafegam por um intermediário
Afinidade de sessãoO balanceador manda cada cliente sempre para a mesma instânciaAjuda, mas não resolve: o evento pode nascer em outra instância
Serviço gerenciadoPusher, Ably, AWS AppSync, Azure Web PubSub cuidam das conexõesQuando operar conexões longas não compensa

A publicação e assinatura do Redis não guarda mensagens: quem não estiver assinando no momento perde o evento. Para a retomada funcionar depois de uma reconexão, os eventos recentes precisam ficar guardados em algum lugar.

Não perder eventos

TécnicaComo funciona
Identificador em cada eventoO SSE envia id:; no WebSocket, um campo na mensagem
Retomada a partir do último recebidoNo SSE, o navegador envia Last-Event-ID sozinho; no WebSocket, o cliente informa o último identificador ao reconectar
Eventos recentes guardadosUm Redis Stream, uma tabela de eventos ou um log, de onde o servidor reenvia o que o cliente perdeu
Estado completo ao conectarEm vez de reenviar eventos, o servidor manda o estado atual (o status do pedido agora) e só depois os eventos novos

A última técnica costuma ser a mais simples e a mais robusta: o cliente que reconecta recebe o estado atual, e não precisa de todos os eventos intermediários. Ela funciona bem para status e painéis; não serve quando cada evento importa, como as mensagens de um chat.

Proteger as conexões

RiscoDefesa
Conexões abertas sem autenticaçãoAutenticar na abertura e encerrar quando a credencial vencer
Um cliente abrindo milhares de conexõesLimite de conexões por usuário e por IP
Um cliente inundando o WebSocket de mensagensLimite de mensagens por segundo, e tamanho máximo de mensagem
Origem não confiável abrindo WebSocket com o cookie do usuárioConferir o cabeçalho Origin na abertura: o WebSocket não segue as regras de CORS
Token na URL do WebSocketAparece em logs de proxies; prefira um token de vida curta, válido só para abrir a conexão

A linha sobre Origin merece atenção: o navegador envia os cookies do usuário ao abrir um WebSocket para qualquer domínio, e o CORS não bloqueia. Sem conferir a origem, uma página maliciosa pode abrir uma conexão autenticada em nome do usuário.

Dimensionar para conexões longas

Uma conexão de tempo real fica aberta por minutos ou horas, ao contrário de um pedido comum, que dura milissegundos. Isso muda a conta de capacidade:

RecursoEfeito
Descritores de arquivoCada conexão ocupa um; o limite do processo precisa comportar todas, como mostra o tópico de Linux
MemóriaCada conexão tem buffers e estado; dezenas de milhares somam gigabytes
ThreadsModelos de uma thread por conexão não escalam; laços de eventos, goroutines e threads virtuais sim
BalanceadorPrecisa de tempo de ociosidade maior que o intervalo dos pulsos
DeploysCada deploy derruba as conexões da instância substituída; os clientes reconectam todos de uma vez

O último ponto pede cuidado: um deploy que derruba 50 mil conexões gera 50 mil reconexões em segundos. Uma variação aleatória no intervalo de reconexão dos clientes espalha essa onda. No SSE, o campo retry: define o intervalo; no WebSocket, a lógica de reconexão fica no cliente.

Resumo da decisão

  1. SSE para atualizações do servidor ao cliente; WebSocket quando o cliente também envia muito; consultas quando as atualizações são raras.
  2. Publicação e assinatura entre as instâncias, para o evento chegar a quem está conectado em outra.
  3. Retomada: identificador por evento e, de preferência, o estado atual na reconexão.
  4. Uma rota comum com o estado atual, como reserva.
  5. Pulsos abaixo do tempo de ociosidade do balanceador; nada de buffer ou compressão no caminho.
  6. Segurança na abertura: autenticação, Origin no WebSocket, limites de conexões e mensagens.

O próximo nível mostra um servidor SSE nas cinco linguagens, executado com e sem Last-Event-ID, e um servidor WebSocket com pulsos.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Escolher a forma de tempo real

    Preciso acrescentar atualizações em tempo real ao meu sistema e quero escolher a forma certa.
    
    Pergunte o que precisa ser atualizado, com que frequência, quantos clientes simultâneos são esperados, se o cliente também envia dados com frequência e qual é a infraestrutura (balanceador, proxy, CDN).
    
    Compare para o meu caso consultas periódicas, SSE e WebSocket, e recomende uma forma, explicando:
    1. Como os eventos chegam à instância em que o cliente está conectado.
    2. Como a reconexão e a retomada vão funcionar.
    3. Como fica a autenticação.
    4. O que muda na configuração da infraestrutura.
  • Tempo real com várias instâncias

    Minha aplicação roda em várias instâncias e quero que os eventos de tempo real cheguem aos clientes certos.
    
    Pergunte qual linguagem eu uso e se já existe Redis, Kafka ou outro intermediário.
    
    Explique:
    1. Por que um evento gerado numa instância não chega sozinho aos clientes de outra.
    2. Como usar publicação e assinatura do Redis para repassar os eventos.
    3. Por que a publicação e assinatura do Redis não guarda mensagens, e como guardar os eventos recentes para a retomada (Redis Streams, tabela de eventos).
    4. A alternativa de mandar o estado atual na reconexão.
    5. Quando um serviço gerenciado compensa mais que operar as conexões.
  • Segurança em conexões de tempo real

    Quero proteger as conexões SSE ou WebSocket da minha aplicação.
    
    Explique:
    1. Como autenticar na abertura, com cookie, token de vida curta ou primeira mensagem, e os riscos de cada um.
    2. Como encerrar a conexão quando a credencial vence.
    3. Por que o WebSocket não segue as regras de CORS e como conferir o cabeçalho Origin.
    4. Limites de conexões por usuário e de mensagens por segundo.
    5. Como validar as mensagens recebidas pelo WebSocket.
    
    Pergunte qual linguagem e biblioteca eu uso e mostre o código para o meu caso.