Server-Sent Events em Rust: enviando dados em tempo real sem WebSocket

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Quando WebSocket é demais, ou por que SSE está de volta à moda

\n\n

Imagine: você precisa enviar dados do servidor para o cliente em tempo real. Cotações de moedas, notificações de novas mensagens, logs do servidor, progresso do processamento de arquivos. A primeira coisa que vem à mente é o WebSocket. Mas e se você não precisar de um canal bidirecional? E se o cliente apenas escuta e o servidor apenas fala?

\n\n

É aqui que entram em cena os Server-Sent Events (SSE) — um protocolo que permite ao servidor enviar dados ao cliente através de uma conexão HTTP comum. Sem handshakes extras, sem protocolos complexos, sem dores de cabeça com proxies e balanceadores de carga. E sim, em Rust isso funciona de forma simplesmente divina.

\n\n

Neste artigo, vamos explorar como configurar SSE em Rust do zero, usando frameworks populares. Você verá exemplos funcionais, conhecerá as armadilhas e entenderá quando SSE é a sua melhor escolha.

\n\n

O que são Server-Sent Events e por que Rust?

\n\n

SSE é um padrão W3C (EventSource API) que permite ao servidor enviar dados em streaming para o cliente via HTTP. O cliente se conecta a um endpoint e o servidor começa a enviar mensagens no formato text/event-stream. Tudo funciona sobre HTTP/1.1 ou HTTP/2 comum, não requer proxies especiais e é suportado por todos os navegadores modernos.

\n\n

Por que Rust? Porque Rust nos oferece:

\n
    \n
  • Assincronicidade sem surpresas — Tokio e async-std permitem manter milhares de conexões SSE em uma única thread
  • \n
  • Segurança de memória — sem vazamentos, sem race conditions em streaming multithread
  • \n
  • Consumo mínimo de recursos — uma conexão SSE consome alguns kilobytes
  • \n
  • Ecossistema — Actix Web, Rocket, Axum — todos suportam SSE nativamente ou com extensões mínimas
  • \n
\n\n

Como funciona o SSE: analisando o protocolo

\n\n

O cliente cria um EventSource no navegador, especificando uma URL. O servidor responde com o cabeçalho Content-Type: text/event-stream e começa a enviar linhas no formato:

\n\n
data: Olá, mundo!\\n\\n
\n\n

Cada mensagem é um bloco onde data: contém a carga útil. As mensagens são separadas por dois caracteres de nova linha. É possível transmitir JSON, números, strings — o que você quiser. Além disso, existem os campos id, event e retry.

\n\n

Aqui está um exemplo mínimo de mensagem SSE:

\n\n
id: 1\nevent: update\ndata: {"status": "processing", "progress": 42}\n\n
\n\n

O cliente em JavaScript recebe isso como um evento update com o campo data contendo JSON.

\n\n

Escrevendo um servidor SSE em Rust com Actix Web

\n\n

Vamos começar com o framework mais popular — Actix Web. Instale as dependências:

\n\n
[dependencies]\nactix-web = "4"\nactix-rt = "2"\ntokio = { version = "1", features = ["full"] }\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"
\n\n

Agora vamos escrever um endpoint simples que enviará a hora atual a cada 2 segundos:

\n\n
use actix_web::{get, web, App, HttpRequest, HttpResponse, HttpServer, middleware};\nuse actix_web::rt::time;\nuse futures::stream::StreamExt;\nuse std::time::Duration;\n\n#[get("/events")]\nasync fn stream_events(req: HttpRequest) -> HttpResponse {\n    let mut rx = tokio::sync::broadcast::channel::<String>(100).1;\n\n    let stream = async_stream::stream! {\n        loop {\n            let message = format!("data: {}\\n\\n", chrono::Local::now());\n            yield Ok::<_, actix_web::Error>(\n                web::Bytes::from(message)\n            );\n            time::sleep(Duration::from_secs(2)).await;\n        }\n    };\n\n    HttpResponse::Ok()\n        .insert_header(("Content-Type", "text/event-stream"))\n        .insert_header(("Cache-Control", "no-cache"))\n        .insert_header(("Connection"                

Blogs

Book Recommendations