"Real-time na web" não é uma tecnologia, são quatro, com semantics diferentes, custos diferentes, e armadilhas próprias. WebSockets, SSE, long polling, WebRTC. Cada um responde a um cenário específico. Devs frequentemente escolhem WebSocket por reflexo, paga complexidade que não precisa; outros usam polling pelo medo de WS, perdem performance.
Este módulo é real-time com clareza: protocolo (frames, ping/pong, reconnect), uso adequado, escala (sticky sessions, fan-out), back-pressure, autenticação no upgrade, e quando usar cada. Você sai sabendo desenhar um sistema com pushing real, não polling disfarçado.
2. Teoria Hard
2.1 Espectro de "real-time"
Polling clássico: client pergunta a cada N segundos. Custoso, latente, mas trivial.
Long polling: client manda request; server segura até ter resposta ou timeout. Próximo step. Funciona em qualquer infra HTTP.
Server-Sent Events (SSE): server faz push unidirecional sobre HTTP/1.1 ou HTTP/2. Reconnect automático.
WebSockets (WS): full-duplex bidirecional sobre HTTP upgrade.
WebRTC: P2P entre clients, com servidor só pra signaling.
HTTP/2 push (DEPRECATED): foi removida do mainstream, não use.
HTTP/3 + WebTransport: novo, baseado em QUIC. Estável mas adoção nascente.
Escolha por direção e frequência:
Server → client raro: SSE ou polling.
Server → client frequente, unidirecional: SSE.
Client → server frequente OU bidirecional: WebSocket.
Cliente↔cliente com baixa latência (voz, vídeo): WebRTC.
2.2 Long polling
Client faz GET /events?since=X. Server compara estado; se há novidade, responde; senão, segura request até timeout (~30s) ou novo evento.
Vantagens:
Funciona em qualquer infra HTTP.
Reverso ao polling: latência baixa quando há eventos.
Desvantagens:
Conexão consumida durante hold.
Não é "real-time" puro, overhead de reconnect a cada evento.
Cuidado com proxies (timeouts, buffering).
Usado como fallback quando WS bloqueado.
2.3 SSE, Server-Sent Events
Spec MDN/W3C. Stream de eventos texto, framing trivial (\n\n separa). API browser:
const es = new EventSource('/orders/stream');
es.onmessage = (e) => console.log(e.data);
es.addEventListener('order_paid', (e) => { ... });
Reconnect automático com Last-Event-ID header (server pode resumir).
Trafega sobre HTTP normal, passa por CDN, proxies geralmente.
Push barato.
Desvantagens:
Unidirecional (server → client). Cliente envia via fetch normal.
Default browser limita 6 conexões por origin em HTTP/1.1. HTTP/2/3 levanta isso.
IE/old-mobile sem suporte (irrelevante em 2026).
Quando vence: notificações push, status updates, dashboards live, AI streaming responses (ChatGPT-style). SSE é underrated: em 2026 voltou a ser padrão pra LLM streaming.
uWebSockets.js: C++ bindings, throughput muito alto.
socket.io: alto-nível, com fallback (long polling), rooms, namespaces, ack callbacks. Próprio protocol em cima de WS, cliente precisa ser socket.io. Não é raw WS.
Bun.serve com websocket: built-in em Bun.
Hono upgradeWebSocket + adapters.
Em greenfield, decide:
WS puro (ws ou framework): se você quer protocol limpo e suas próprias semantics.
Socket.io: se rooms/namespaces/auto-reconnect/fallback são DX que vale dependência.
2.7 Autenticação no upgrade
WebSocket abre via HTTP, cookies/headers do upgrade são acessíveis ao server. Padrões:
Cookie HttpOnly de session (mesmo que HTTP), server lê no upgrade, valida.
JWT em query string (/ws?token=...), funciona, mas evite (logs).
JWT em first message: depois do connect, cliente manda {auth: token}; server valida ou fecha.
Subprotocol header: alguns trafegam token em Sec-WebSocket-Protocol.
Em mobile (RN), cookies trickier, mais comum first-message auth.
2.8 WebSocket em scale
Single server: trivial.
N servers: cliente conecta em qualquer um. Quando user A no server 1 manda mensagem pra user B no server 2, precisa fan-out.
Patterns:
Pub/Sub via Redis: server 1 publica chat:userB; server 2 está subscribed; entrega ao socket local.
Sticky sessions: load balancer roteia mesmo cliente sempre pro mesmo server (via cookie). Reduz movement mas não resolve fan-out.
Managed: Pusher, Ably, Soketi (open-source compat com Pusher).
Trade-off: servers mantêm estado (lista de sockets). Restart drops conexões. Clients reconectam.
2.9 Sticky session e load balancer
L4 (TCP) load balancer: cookies não enxergáveis; sticky por IP. Funciona até NAT.
L7 (HTTP) com cookie: balancer adiciona cookie lb=server-3. Roteamento estável.
CDNs (Cloudflare) suportam WebSocket; trace Cf-Ray se aparecerem problemas.
2.10 SSE em scale
Mesma lógica de fan-out (Redis pub/sub) aplica. Mais simples por ser unidirecional.
CDN compat: SSE deveria passar, mas configure pra não bufferizar (X-Accel-Buffering: no em Nginx, Cache-Control: no-cache).
2.11 GraphQL Subscriptions
Subscriptions em GraphQL são tipicamente backed por WebSocket (graphql-ws subprotocol). Lib server: graphql-ws, deprecates antigo subscriptions-transport-ws.
Fluxo: cliente abre WS, manda {type: "subscribe", id: 1, payload: {query: ...}}, server emite {type: "next", id: 1, payload: {data: ...}} em loop, fim com {type: "complete"}.
2.12 WebRTC
P2P real-time entre browsers. Casos: voz/vídeo, file transfer, low-latency game data.
Componentes:
RTCPeerConnection: sessão.
SDP: descrição de oferta/resposta (codecs, candidatos ICE).
ICE / STUN / TURN: descoberta de network paths. STUN dá IP público; TURN é relay quando NAT impede P2P.
Signaling server: você implementa (WS, SSE) pra trocar SDP/ICE entre peers. WebRTC não define.
Data Channels: canal arbitrário (não só audio/video).
Complexidade alta. Em projetos onde voz/vídeo é core, considere SaaS (Twilio, LiveKit, Daily, Agora) ou self-hosted SFU (mediasoup, Janus, Jitsi).
Em logística: WebRTC pra audio entre courier e cliente quando há entrega complicada. Provavelmente não vale build próprio; integre LiveKit ou similar.
At-most-once: send and forget. Pode perder. Trivial.
At-least-once: ack + retry. Pode duplicar. Cliente idempotente.
Exactly-once: dedup id + commit em store. Mais caro.
WebSocket bruto é at-most-once. Protocols sobre (graphql-ws, custom) podem implementar acks.
Pra notificações user-facing simples, at-most-once geralmente ok. Pra ações críticas (pagamento), use HTTP + idempotency, não WS.
2.14 WebTransport, deep
Sobre HTTP/3 / QUIC (01-03 §2.6.1). Em 2025-2026 saiu de "experimental" pra suportado em Chrome/Edge (default), Firefox (release recente), Safari (em desenvolvimento). Substitui WebSocket em casos onde HOL blocking importa ou onde você precisa datagram unreliable.
Modelo mental:
WebTransport = conexão QUIC exposta no browser.
Por conexão você abre N streams + datagram channel.
Cada stream é independente, perda em um não afeta outros (sem TCP HOL blocking).
Node: @fails-components/webtransport (binding pra lsquic em C). Maturidade média.
Rust: wtransport ou quiche. Maduro.
Go: quic-go/webtransport-go. Sólido em produção.
Servidores HTTP/3: nginx 1.25+, Caddy 2.7+, Cloudflare Workers (Durable Objects WebSocket por enquanto, WT em roadmap).
Pegadinhas reais:
TLS 1.3 obrigatório + QUIC = nem sempre passa em redes corporativas com firewalls UDP-blocking. Tenha fallback pra WebSocket.
CORS-like origin checking: server precisa validar origin do client.
Stateful proxies/CDN: muitos CDNs ainda não passam WebTransport em 2026. Cloudflare e Fastly sim, outros não.
Reconnect: ao contrário de WebSocket que tem semantics simples (connection drop = reopen), WebTransport tem reconnect por stream + connection migration QUIC. Lib client geralmente abstrai.
Veredicto:
App genérico de chat/notificação em 2026: WebSocket continua sendo o pragmático. Maturidade total, suporte universal.
Game multiplayer, live editor colaborativo, vídeo realtime, telemetry: vale WebTransport. Resolve dores reais de WebSocket.
Edge functions / serverless: WebSocket via Durable Objects (Cloudflare) ou similar; WebTransport em edge ainda nascente.
2.15 Edge runtime e WebSocket
Cloudflare Workers: suporta WebSocket via Durable Objects. Each connection sticks to a Durable Object. Pattern: 1 DO por "room".
Vercel Edge: WebSocket suportado em algumas configs, melhor via Edge Functions com fetch streams.
AWS Lambda: não tem WS persistente; API Gateway WebSocket mantém conexão e invoca Lambda em events.
Em projetos sérios de real-time, considere serviço dedicado (Soketi, Centrifugo, Pusher, Ably) em vez de tentar empurrar WS em runtime stateless.
2.16 Push notifications mobile
Diferente de real-time em-app. Push notifications via APN (iOS) e FCM (Android), chegam mesmo com app fechado. Cobertos no 02-06.
Combinação real: WebSocket quando app aberto + Push quando fechado.
3. Threshold de Maestria
Você precisa, sem consultar:
Comparar long polling, SSE, WebSocket e WebRTC em direção, latência e custo de infra.
Explicar o handshake WebSocket: que headers, que código de status.
Implementar reconnect exponencial com jitter em pseudo-código.
Distinguir auth no upgrade WS via cookie e via first-message JWT, com pros/contras.
Explicar fan-out via Redis pub/sub com diagrama.
Justificar SSE pra streaming LLM em vez de WS.
Configurar SSE atrás de Nginx/CDN sem buffering.
Discriminar at-most-once, at-least-once, exactly-once com 1 caso pra cada.
Citar 2 cenários onde WebRTC vence WS, e 2 onde build próprio é loucura.
Explicar limitação de WS em Lambda e como API Gateway WebSocket compensa.
4. Desafio de Engenharia
Adicionar real-time tracking ao Logística, courier streama posição, lojista vê em tempo real.