Voltar ao blog
Desenvolvimento11 min de leitura

Por dentro do DN Tracker: como entregamos posições GPS em tempo real

DN

Diego Nunes

Engineering

D
Desenvolvimento

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.

javascript
// 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.

O servidor envia um evento de heartbeat a cada 15 segundos para manter a conexão viva através de proxies com timeout agressivo. O cliente ignora esses eventos.

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.

DN

Escrito por Diego Nunes

Engineering · DN Tracker

Começar grátis

Gostou? Receba mais artigos por e-mail

Sem spam. Conteúdo sobre rastreamento, frotas e segurança uma vez por semana.