⚡ Curso de HTML • Web-ZoneWare

HTML5 Server-Sent Events (SSE): Guia | Web Academy

Lição 40 de 50

HTML5 Server-Sent Events (SSE): Guia | Web Academy

Tradicionalmente na web, a comunicação segue o modelo estrito de Requisição e Resposta: o navegador do cliente precisa pedir ativamente uma informação para que o servidor possa responder. No entanto, para feeds de notícias, placares esportivos, painéis financeiros e notificações, essa mecânica é ineficiente. A especificação do HTML5 introduziu os Server-Sent Events (SSE), permitindo que o servidor mantenha uma conexão HTTP persistente aberta e “empurre” dados instantaneamente para o navegador (Push Unidirecional) no momento exato em que um evento acontece.

1. Adeus ao Polling Primitivo: A Eficiência do SSE

Antes do SSE, os desenvolvedores eram obrigados a recorrer a técnicas ineficientes como o Short Polling (disparar requisições fetch() a cada 3 segundos via setInterval) ou o Long Polling (manter a requisição suspensa até haver resposta).

Esse modelo tradicional gerava problemas graves de infraestrutura:

  • Desperdício colossal de largura de banda com o reenvio repetitivo de cabeçalhos HTTP e cookies.
  • Sobrecarga inútil da CPU do servidor processando milhares de requisições que retornavam respostas vazias.
  • Latência artificial na entrega de informações críticas ao usuário.

💡 A Elegância do Server-Sent Events

Com o SSE, o cliente faz uma única requisição HTTP inicial. O servidor responde com o tipo especial Content-Type: text/event-stream e mantém o canal aberto. A partir desse instante, sempre que surgir um dado novo, o servidor apenas transmite o fluxo de bytes pela conexão já estabelecida.

2. A Anatomia do Protocolo SSE (text/event-stream)

Diferente de protocolos binários complexos, o SSE é composto por texto puro formatado em UTF-8. Cada mensagem transmitida pelo servidor obedece a regras simples:

CODE 11 linhas
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache
Connection: keep-alive
 
data: {"ativo": "BTC/USD", "preco": 68450.00}
 
event: alerta
data: Volatilidade alta detectada no mercado!
id: 1042
retry: 5000

Campos Padronizados pelo Protocolo:

  • data: O corpo da mensagem ou payload útil (pode ser texto simples ou uma string JSON).
  • event: O nome de um evento customizado. Permite escutar tipos diferentes de ações no cliente.
  • id: Identificador único do evento. Se a internet do usuário oscilar e cair, o navegador envia automaticamente o cabeçalho Last-Event-ID na reconexão, garantindo que nenhuma mensagem seja perdida!
  • retry: Tempo em milissegundos que o navegador deve aguardar antes de tentar reconectar automaticamente caso a conexão seja interrompida.
  • \n\n (Dupla quebra de linha): Sinaliza ao navegador o término do pacote e dispara o processamento do evento.

3. A API EventSource no Navegador (Cliente)

No JavaScript do front-end, consumir uma transmissão SSE é extremamente simples através da classe nativa EventSource:

JavaScript 26 linhas
// 1. Abre a conexão com o endpoint de streaming do servidor
const streamNoticias = new EventSource('/api/cotacoes-tempo-real');
 
// 2. Conexão aberta com sucesso
streamNoticias.onopen = function() {
    console.log('✅ Conexão SSE estabelecida com sucesso.');
};
 
// 3. Recebimento de mensagens genéricas (campos 'data:')
streamNoticias.onmessage = function(event) {
    const dados = JSON.parse(event.data);
    console.log('Nova cotação recebida:', dados);
};
 
// 4. Escuta de eventos tipados específicos (campo 'event: alerta')
streamNoticias.addEventListener('alerta', function(event) {
    console.warn('⚠️ Alerta prioritário do sistema:', event.data);
});
 
// 5. Tratamento de falhas e reconexão automática
streamNoticias.onerror = function(erro) {
    console.log('Conexão perdida. O navegador tentará reconectar sozinho...');
};
 
// 6. Fechamento manual da conexão quando o usuário sair da tela
// streamNoticias.close();

4. Código Prático Completo: Painel de Cotações em Tempo Real

Abaixo implementamos uma interface de monitoramento com controle de status visual de conexão e exibição de dados recebidos via streaming:

HTML5 18 linhas
<div class="wzw-sse-card">
    <div class="sse-header">
        <h3>📈 Painel de Cotações em Tempo Real (SSE)</h3>
        <span id="statusBadge" class="badge-conectando">Conectando...</span>
    </div>
 
    <div class="price-display">
        <span class="currency-label">Bitcoin (BTC/BRL):</span>
        <span id="valorPreco" class="price-value">R$ 0,00</span>
    </div>
 
    <div class="log-container">
        <h4>Histórico de Atualizações Push:</h4>
        <ul id="listaHistorico" class="sse-logs"></ul>
    </div>
 
    <button id="btnDesconectar" class="btn-sse">Interromper Transmissão</button>
</div>
CSS3 60 linhas
.wzw-sse-card {
    padding: 24px;
    background-color: #0f172a;
    border: 1px solid #1e293b;
    border-radius: 8px;
    color: #f8fafc;
    max-width: 520px;
}
 
.sse-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}
 
.price-display {
    padding: 16px;
    background-color: #1e293b;
    border-radius: 6px;
    margin-bottom: 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
 
.price-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: #10b981;
}
 
.badge-conectando { background: #f59e0b; color: #000; padding: 4px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: bold; }
.badge-online { background: #10b981; color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: bold; }
.badge-offline { background: #ef4444; color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: bold; }
 
.sse-logs {
    list-style: none;
    padding: 0;
    max-height: 140px;
    overflow-y: auto;
    font-size: 0.85rem;
    color: #94a3b8;
}
 
.sse-logs li {
    padding: 6px 0;
    border-bottom: 1px solid #1e293b;
}
 
.btn-sse {
    margin-top: 16px;
    padding: 10px 16px;
    background-color: #ef4444;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-weight: 600;
}
JavaScript 37 linhas
// Script de Demonstração (Simulador de Servidor Local para Testes)
const statusBadge = document.getElementById('statusBadge');
const valorPreco = document.getElementById('valorPreco');
const listaHistorico = document.getElementById('listaHistorico');
const btnDesconectar = document.getElementById('btnDesconectar');
 
// Em um ambiente de produção real, aponte para sua URL de API (ex: '/api/stream-cotacoes')
// const stream = new EventSource('/api/stream-cotacoes');
 
// Para demonstração imediata no navegador, simulamos o comportamento de recebimento:
statusBadge.className = 'badge-online';
statusBadge.textContent = '● Transmitindo ao Vivo';
 
function adicionarLog(texto) {
    const li = document.createElement('li');
    li.textContent = `[${new Date().toLocaleTimeString()}] ${texto}`;
    listaHistorico.prepend(li);
}
 
// Simulação de eventos empurrados pelo servidor a cada 3 segundos
let precoSimulado = 380250.00;
const simuladorInterval = setInterval(() => {
    const variacao = (Math.random() - 0.48) * 1200;
    precoSimulado += variacao;
 
    valorPreco.textContent = precoSimulado.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
    adicionarLog(`Cotação atualizada: ${precoSimulado.toFixed(2)} BRL`);
}, 3000);
 
btnDesconectar.addEventListener('click', () => {
    clearInterval(simuladorInterval);
    // stream.close();
    statusBadge.className = 'badge-offline';
    statusBadge.textContent = 'Desconectado';
    adicionarLog('Conexão encerrada pelo usuário.');
    btnDesconectar.disabled = true;
});
🧪 Quer testar o Server-Sent Events e streaming no navegador?
⚡ Abrir no Playground IDE →

5. Comparativo: Server-Sent Events vs WebSockets

Ambas são tecnologias de tempo real do ecossistema HTML5 moderno, mas com arquiteturas projetadas para necessidades diferentes:

Critério Server-Sent Events (SSE) WebSockets (WS)
Direção do Fluxo Unidirecional (apenas Servidor → Cliente) Totalmente Bidirecional (Cliente ↔ Servidor)
Protocolo HTTP / HTTP/2 padrão (porta 80 ou 443) Protocolo TCP próprio com handshake (ws:// ou wss://)
Reconexão Automática Nativa pelo navegador (sem nenhuma linha de código) Manual (requer bibliotecas ou tratamento via código)
Compatibilidade com Proxies e Firewalls 100% transparente em qualquer proxy ou CDN existente Pode ser bloqueado por firewalls e proxies corporativos restritivos
Cenário Perfeito Feeds de notícias, dashboards, cotações, notificações Jogos online multiplayer, salas de bate-papo, edição colaborativa

💡 Dica Pro: O Limite de Conexões HTTP/1.1 vs HTTP/2

No protocolo legado HTTP/1.1, os navegadores impõem um limite rígido de apenas 6 conexões simultâneas abertas por domínio. Se você abrir 6 abas com SSE, a 7ª aba travará sem conseguir carregar imagens ou páginas!

A solução moderna: Certifique-se de que seu servidor web utilize HTTP/2 ou HTTP/3. Nesses protocolos, múltiplas conexões e streams são multiplexados dentro de uma única conexão TCP física, eliminando totalmente o limite de abas.

6. Ferramentas Relacionadas & Próximos Passos

Continue seu domínio de tecnologias interativas com as ferramentas do nosso ecossistema:

7. Referências Técnicas & Especificações Oficiais


Rolar para cima