Uma visão técnica da nossa arquitetura de streaming usando Server-Sent Events (SSE), o protocolo que permite atualizar o mapa sem polling e com latência abaixo de 2 segundos.
Quando você abre o painel do DN Tracker e vê um veículo se mover no mapa, o que está acontecendo por baixo? Neste artigo, descrevemos a arquitetura de ponta a ponta que entrega posições GPS do dispositivo instalado no veículo até o seu navegador em menos de 2 segundos.
O problema com polling
A abordagem ingênua para dados em tempo real é o polling: o cliente pergunta ao servidor a cada N segundos "há novidades?". Isso funciona, mas tem dois problemas sérios em escala: cada cliente gera uma request por intervalo (100 clientes = 100 req/s para um intervalo de 1s), e a latência média é metade do intervalo de polling.
Para 10.000 veículos com 100 usuários ativos, polling de 5s significa 20 req/s por usuário ativo — inviável sem infraestrutura cara.
Por que SSE e não WebSocket
Server-Sent Events (SSE) é uma especificação HTTP nativa que permite o servidor enviar dados ao cliente sobre uma conexão HTTP persistente. Ao contrário do WebSocket, SSE é unidirecional (servidor → cliente) e funciona sobre HTTP/1.1 e HTTP/2 sem negociação especial.
Para rastreamento veicular, a comunicação é essencialmente unidirecional: o servidor empurra posições, o cliente as exibe. SSE é a ferramenta certa para este caso — mais simples, compatível com proxies e balanceadores de carga HTTP padrão.
Arquitetura do pipeline
- Dispositivo GPS → transmite via MQTT a cada 30s para o tracker service (apps/tracker)
- Tracker service → persiste no banco e publica em canal Redis pub/sub
- Portal SSE handler → assina o canal Redis da organização e repassa eventos para conexões SSE abertas
- Cliente → recebe o evento e atualiza a posição do marcador no mapa
Autenticação do stream
Conexões SSE não suportam headers customizados via EventSource da Web API. Nossa solução: o cliente solicita primeiro um token de curta duração via API REST autenticada, depois abre a conexão SSE passando o token como query parameter.
// 1. Obter token efêmero (válido por 60s)
const { token } = await fetch("/api/positions/sse-token", {
credentials: "include",
}).then((r) => r.json())
// 2. Abrir conexão SSE
const es = new EventSource(`/api/stream?token=${token}&orgId=${orgId}`)
es.addEventListener("position", (e) => {
const pos = JSON.parse(e.data)
updateMarker(pos.deviceId, pos.lat, pos.lng)
})Gerenciamento de conexões
Cada conexão SSE mantém uma assinatura no Redis. Quando o cliente desconecta (fechar aba, perda de rede), o handler detecta o close event e cancela a assinatura. Conexões zumbi são eliminadas por um TTL de 90 segundos sem heartbeat.
Latência medida
Em produção, medimos o tempo entre o timestamp registrado pelo dispositivo e o timestamp de chegada no cliente. A mediana é 1.2s, o p95 é 2.8s. Os principais componentes de latência são: transmissão MQTT (400ms), processamento e persistência (200ms) e entrega SSE (600ms).
Para comparação: polling de 5s teria latência média de 2.5s com muito mais carga no servidor. SSE entrega latência menor com custo operacional significativamente menor em escala.
Escrito por Diego Nunes
Engineering · DN Tracker