WebSockets e SSE
Protocolos para comunicação em tempo real entre browser e servidor, sem que o cliente precise solicitar dados constantemente. WebSocket para fluxo bidirecional; SSE para push unidirecional simples; Long Polling como fallback universal.
Intenção
HTTP clássico é request-response: o cliente pergunta, o servidor responde e a conexão fecha. Para tempo real — chat, jogos, notificações, dashboards ao vivo — esse modelo exige polling contínuo, que desperdiça largura de banda e aumenta a latência. WebSocket, SSE e Long Polling eliminam esse desperdício de formas diferentes.
A escolha entre os três depende principalmente de duas dimensões: a direção do fluxo de dados (bidirecional ou só servidor→cliente) e os requisitos de infraestrutura (proxies corporativos, balanceadores de carga, HTTP/2 suportado ou não).
Problema
O HTTP clássico foi projetado para o modelo "documento na web": o browser pede um recurso, o servidor entrega e a conexão é encerrada. Isso é eficiente para páginas estáticas, mas cria três problemas para aplicações em tempo real:
- Latência de polling: se o browser verifica por novos dados a cada segundo, a latência efetiva é até 1 segundo mesmo quando o servidor tem dados imediatos. Reduzir o intervalo aumenta o custo de rede.
- Overhead por requisição: cada request HTTP carrega headers de dezenas a centenas de bytes (cookies, User-Agent, Accept, etc.). Em uma aplicação de chat com 100 mensagens por minuto, esse overhead supera em muito o conteúdo útil.
- Servidor não consegue iniciar a comunicação: no modelo clássico, o servidor só pode responder, nunca pode notificar. Para "você recebeu uma mensagem" ou "sua ordem foi executada", o cliente precisa perguntar continuamente.
Como funciona
WebSocket — conexão full-duplex persistente
WebSocket começa com um handshake HTTP: o cliente envia uma requisição
com Upgrade: websocket e o servidor responde com
101 Switching Protocols. A partir daí, a conexão TCP
permanece aberta e ambos os lados podem enviar frames
em qualquer direção, a qualquer momento, sem overhead de headers HTTP
por mensagem.
// Handshake HTTP → ws://
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
// A partir daqui: frames binários ou texto em ambas direções
// sem overhead HTTP por mensagem
O frame WebSocket tem overhead mínimo: 2–10 bytes de cabeçalho versus 200–800 bytes de headers HTTP. Em aplicações de alta frequência (trading, jogos), essa diferença é significativa.
Escalabilidade: conexões WebSocket são stateful. O mesmo cliente deve sempre chegar à mesma instância do servidor (sticky session) ou o servidor deve usar um broker externo (Redis Pub/Sub, Kafka) para propagar mensagens entre instâncias — broadcasting funciona somente dentro de uma instância sem coordenação externa.
SSE — Server-Sent Events
SSE usa HTTP comum: o cliente faz um GET e o servidor responde
com Content-Type: text/event-stream, mantendo a conexão aberta
e enviando eventos no formato data: ...\n\n conforme surgem.
É unidirecional: apenas o servidor envia dados; o cliente
usa a EventSource API nativa do browser.
// Servidor envia eventos no formato SSE
data: {"type":"price","value":142.5}\n\n
data: {"type":"price","value":143.1}\n\n
: keepalive comment (evita timeout de proxies)\n\n
data: {"type":"alert","msg":"target atingido"}\n\n
Vantagens do SSE sobre WebSocket para casos unidirecionais:
-
Reconexão automática: se a conexão cair, o browser
reabre automaticamente e envia o último ID recebido via
Last-Event-ID, permitindo que o servidor retome do ponto correto. - HTTP/2 multiplexado: em HTTP/2, múltiplos streams SSE compartilham uma única conexão TCP, eliminando o limite de 6 conexões paralelas do HTTP/1.1.
- Mais simples de implementar e depurar: é HTTP puro, visível nas ferramentas de rede do browser, compatível com qualquer proxy e sem handshake especial.
Long Polling — server push via pull repetitivo
Long Polling simula server push sem protocolo especializado: o browser faz um request HTTP normal, mas o servidor não responde imediatamente — ele segura a conexão aberta até ter dados para enviar (ou até um timeout configurado). Quando responde, o browser imediatamente abre um novo request para esperar a próxima atualização.
É a abordagem de maior overhead (headers HTTP completos a cada ciclo) e maior latência (latência = tempo de resposta do servidor + tempo de estabelecer novo request), mas funciona em qualquer proxy corporativo e qualquer ambiente de rede restrito, o que a torna um fallback confiável.
Tabela comparativa
WebSocket SSE Long Polling
─────────────────────────────────────────────────────────────────────
Protocolo ws:// / wss:// HTTP HTTP
Direção Full-duplex Servidor → cliente Servidor → cliente
Overhead/msg 2–10 bytes ~50 bytes 200–800 bytes
Reconexão auto Não (manual) Sim (nativa) Não (manual)
HTTP/2 Não compatível Compatível Compatível
Proxies corp. Pode bloquear Geralmente OK Sempre funciona
Complexidade Média Baixa Baixa
─────────────────────────────────────────────────────────────────────
Ideal para Chat, jogos, Notificações, Fallback, envs
colaboração, feeds, progresso, restritos
trading, IoT dashboards ao vivo corporativos
Escalabilidade WebSocket com múltiplas instâncias
WebSocket cria conexões stateful: se o usuário A está conectado à instância 1 e o usuário B está na instância 2, uma mensagem do usuário B para o usuário A não chega por padrão — o servidor da instância 1 não sabe que a instância 2 tem algo para entregar.
As duas soluções padrão:
- Sticky session: o balanceador garante que o mesmo cliente sempre chega à mesma instância. Simples, mas reduz a efetividade do load balancing e complica deploys sem downtime.
- Pub/Sub externo: cada instância publica mensagens em um broker (Redis Pub/Sub, Kafka, NATS) e assina os tópicos dos seus clientes conectados. Qualquer instância pode receber uma mensagem e entregá-la ao cliente correto. Mais robusto e escalável, porém mais complexo de operar.
Quando usar cada protocolo
- WebSocket: quando há comunicação genuinamente bidirecional em tempo real — chat, edição colaborativa, jogos multiplayer, dashboards de trading onde o cliente também envia dados frequentemente (posição do cursor, comandos de negociação). Latência mínima é essencial.
- SSE: quando o servidor precisa enviar atualizações ao cliente mas o cliente não envia dados de volta continuamente. Notificações, feeds de atividade, barra de progresso de jobs, dashboards de monitoramento. Prefira SSE a WebSocket nesses casos — é mais simples e aproveita HTTP/2.
- Long Polling: use como fallback quando SSE não funciona (proxies corporativos que bufferizam respostas, ambientes com suporte antigo). Muitas bibliotecas (Socket.IO, SignalR) implementam Long Polling como fallback automático.
Quando evitar
- WebSocket quando SSE seria suficiente: WebSocket tem mais overhead operacional, exige sticky session ou pub/sub, e não aproveita HTTP/2. Para fluxo unidirecional, SSE é a escolha mais simples e robusta.
- WebSocket para 1 M+ conexões simultâneas sem arquitetura dedicada: cada conexão mantém estado no servidor. Em alta escala, isso exige servidores de gateway dedicados (como o Pushpin, Fanout ou arquiteturas event-driven específicas), não servidores de aplicação convencionais.
Prós e contras
WebSocket — Prós
- Full-duplex real: ambos os lados enviam dados sem esperar o outro terminar.
- Overhead mínimo por mensagem (2–10 bytes vs. 200+ de headers HTTP).
- Latência mais baixa que qualquer solução baseada em HTTP clássico.
- Suportado nativamente em todos os browsers modernos e a maioria dos frameworks.
WebSocket — Contras
- Stateful: exige sticky session ou pub/sub externo para múltiplas instâncias.
- Não compatível com HTTP/2 multiplexing.
- Alguns proxies corporativos bloqueiam ou inspecionam ws:// — use sempre wss://.
- Reconexão precisa ser implementada manualmente no cliente.
SSE — Prós
- HTTP puro: transparente para proxies, balanceadores e ferramentas de rede.
- Reconexão automática nativa via EventSource + Last-Event-ID.
- Aproveita HTTP/2: múltiplos streams numa única conexão TCP.
- Mais simples de implementar e depurar que WebSocket.
SSE — Contras
- Unidirecional: o cliente não pode enviar dados pelo stream SSE — precisa de requests HTTP separados.
- Alguns proxies antigos bufferizam a resposta, quebrando o streaming (mitigável com comentários de keepalive).
Armadilhas comuns
1. WebSocket sem sticky session e sem pub/sub
O erro mais comum em deploys horizontais: o usuário A conecta à instância 1, o usuário B conecta à instância 2, e quando B envia uma mensagem para A, a instância 2 tenta entregá-la — mas A não está conectado lá. A mensagem é perdida silenciosamente. O sintoma é "às vezes as mensagens não chegam" e piora exatamente quando há mais tráfego (e portanto mais instâncias).
Solução: antes de escalar horizontalmente, implemente Redis Pub/Sub ou equivalente. Cada instância publica mensagens no canal do destinatário e todas as instâncias assinam todos os canais dos seus clientes conectados.
2. ws:// sem TLS em produção
Conexões ws:// (sem TLS) são transparentes para proxies
intermediários, que podem injetar conteúdo, modificar frames ou simplesmente
bloquear a conexão. Em produção, use sempre wss:// (WebSocket
sobre TLS), que é tratado como HTTPS pelos proxies.
3. SSE bloqueado por proxies corporativos
Alguns proxies corporativos bufferizam respostas HTTP antes de repassar ao
cliente, o que quebra o streaming do SSE (o cliente nunca recebe eventos
intermediários, só o final). A mitigação é enviar comentários periódicos
(: keepalive\n\n) a cada 15–30 segundos, que forçam o flush
em muitos proxies. Se não resolver, Long Polling é o fallback.
4. Escala de conexões subestimada
10 000 conexões WebSocket simultâneas são perfeitamente gerenciáveis em um
servidor node.js bem configurado. 1 000 000 de conexões é uma arquitetura
completamente diferente: exige servidores de gateway dedicados, kernel
tuning (ulimit, net.core.somaxconn), e possivelmente
serviços gerenciados (AWS API Gateway WebSocket, Ably, Pusher). Não trate
10 k e 1 M como o mesmo problema.
Arquiteturas e padrões relacionados
Filas de Mensagem complementam WebSocket na camada de backend: o servidor de websocket recebe mensagens dos clientes, publica numa fila e processa assincronamente, desacoplando a camada de conexão da camada de processamento.
Load Balancing é diretamente afetado pela escolha de protocolo: WebSocket exige sticky sessions ou algoritmos de balanceamento que respeitem afinidade de sessão (IP hash, cookie-based), enquanto SSE funciona com qualquer algoritmo de balanceamento, incluindo round-robin puro.
REST vs GraphQL vs gRPC cobre os protocolos síncronos request-response. WebSocket e SSE são complementares a eles — muitas arquiteturas usam REST para operações CRUD e WebSocket/SSE para atualizações em tempo real.