Software · Infraestrutura & Sistemas
Server-Sent Events, WebSockets e Long Polling: Comunicação em Tempo Real
Como escolher o transporte adequado para fluxos contínuos de dados: o overhead do Long Polling, a bidirecionalidade do WebSocket e a simplicidade de Server-Sent Events (SSE) em aplicações de streaming e inteligência artificial.
Neste artigo
A web tradicional foi desenhada sobre um paradigma estritamente reativo de requisição e resposta iniciado pelo cliente. Quando as aplicações passaram a exigir atualizações contínuas em tempo real — como cotações financeiras, notificações de bate-papo e, mais recentemente, o streaming token a token de respostas de modelos de inteligência artificial —, a engenharia precisou inventar maneiras de empurrar dados do servidor para o navegador sem exigir que o usuário recarregasse a página.
Ao longo dessa evolução, três tecnologias principais se estabeleceram: Long Polling, WebSockets e Server-Sent Events (SSE). Embora muitos desenvolvedores recorram por hábito ao WebSocket para qualquer necessidade de atualização em tempo real, essa escolha frequentemente adiciona complexidade desnecessária quando a comunicação precisa fluir em apenas uma direção.
flowchart TD
subgraph LP["1. Long Polling (Legado)"]
ClientLP["Cliente"] -->|Requisição HTTP| SrvLP["Servidor aguarda evento"]
SrvLP -->|Fecha resposta com dado| ClientLP
ClientLP -.->|Abre nova requisição imediata| SrvLP
end
subgraph WS["2. WebSocket (Bidirecional)"]
ClientWS["Cliente"] <-->|Upgrade HTTP para Túnel TCP Full-Duplex| SrvWS["Servidor"]
end
subgraph SSE_D["3. Server-Sent Events (Unidirecional Contínuo)"]
ClientSSE["Cliente"] -->|GET /stream (HTTP/2)| SrvSSE["Servidor"]
SrvSSE -->>|text/event-stream contínuo| ClientSSE
end
Comparativo Técnico de Protocolos
A escolha entre os três padrões deve levar em consideração a direcionalidade do tráfego, a compatibilidade com firewalls e a infraestrutura de balanceamento de carga:
| Critério | Long Polling | WebSocket | Server-Sent Events (SSE) |
|---|---|---|---|
| Direção dos Dados | Bidirecional (emulado via múltiplas requisições) | Full-Duplex (Cliente e Servidor enviam a qualquer momento) | Unidirecional (Apenas Servidor envia para Cliente) |
| Protocolo de Transporte | HTTP padrão | Protocolo binário próprio (ws:// ou wss://) |
HTTP/1.1 ou HTTP/2 padrão (text/event-stream) |
| Reconexão Automática | Manual no código JavaScript | Manual no código (requer heartbeat / ping-pong) | Nativa no navegador (com cabeçalho Last-Event-ID) |
| Passagem por Proxies/Firewalls | Excelente (usa portas HTTP/HTTPS normais) | Pode ser bloqueado ou encerrado por proxies intermediários | Excelente (tráfego HTTP/HTTPS convencional) |
| Sobrecarga de Headers | Altíssima (reenvia headers e cookies em cada ciclo) | Mínima (overhead de apenas 2 a 10 bytes por frame) | Mínima (headers enviados apenas na abertura da conexão) |
1. O Formato de Server-Sent Events no Fio
O SSE funciona sobre uma conexão HTTP convencional mantida aberta indefinidamente com o MIME-type text/event-stream. A especificação define um formato de texto plano extremamente enxuto baseado em campos separados por quebra de linha:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
id: 101
event: token
data: {"texto": "Arquitetura"}
id: 102
event: token
data: {"texto": " de software"}
: este é um comentário de heartbeat (evita timeout de proxy)
id: 103
event: done
data: [DONE]
Reconexão Resiliente Nativa
Se a conexão física de rede cair temporariamente, o navegador aguarda alguns segundos e reconecta automaticamente enviando o cabeçalho HTTP Last-Event-ID: 102. O servidor lê esse valor e reenvia apenas as mensagens a partir do evento 103, garantindo entrega sem perdas e sem exigir lógica complexa de retry no cliente.
No navegador, consumir um fluxo de SSE exige poucas linhas usando a API nativa EventSource:
const eventSource = new EventSource("/api/ia/stream");
// Escuta eventos personalizados emitidos pelo servidor
eventSource.addEventListener("token", (event) => {
const payload = JSON.parse(event.data);
process.stdout.write(payload.texto);
});
eventSource.addEventListener("done", () => {
eventSource.close();
});
eventSource.onerror = (err) => {
console.error("Falha temporária de rede; o navegador reconectará automaticamente.");
};
A Armadilha do Buffer em Proxies Reversos
Um erro muito comum ao colocar SSE em produção com Nginx ou Azure Application Gateway é a mensagem não chegar em tempo real ao navegador, sendo descarregada apenas em blocos grandes de 4 KB ou no encerramento da resposta.
Isso ocorre porque proxies reversos vêm configurados de fábrica para acumular respostas em buffer antes de transmiti-las ao cliente. Para desabilitar o buffer e forçar o envio imediato (flush), o servidor deve retornar o cabeçalho específico:
X-Accel-Buffering: no
Além disso, em servidores Node.js ou ASP.NET Core, é necessário invocar explicitamente o método de descarregamento (response.flush() ou HttpResponse.Body.FlushAsync()) logo após a escrita de cada evento.
Quando WebSockets são Indispensáveis
Embora o SSE seja a escolha perfeita para feeds de notícias, dashboards de telemetria e geração de texto de LLMs, o WebSocket continua sendo insubstituível em dois cenários críticos:
- Jogos Multiplayer em Tempo Real: Onde o cliente precisa enviar centenas de coordenadas e comandos por segundo para o servidor com a menor latência de rede possível.
- Edição Colaborativa e Telas Compartilhadas: Onde múltiplos usuários mutam o mesmo estado compartilhado de forma contínua e bidirecional (como ferramentas no estilo Figma ou quadros brancos digitais).
Para qualquer outro cenário em que o usuário faz perguntas esporádicas e apenas recebe um fluxo volumoso e contínuo de atualizações do servidor, Server-Sent Events sobre HTTP/2 oferece uma solução muito mais estável, fácil de depurar e compatível com as ferramentas existentes de cache e segurança de rede.