WebSockets & SSE
Envie atualizações ao vivo para clientes com WebSockets para chat bidirecional e Server-Sent Events para streams unidirecionais.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use axum::{extract::ws::{Message, WebSocket, WebSocketUpgrade}, response::IntoResponse, routing::get};
async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}
async fn handle_socket(mut socket: WebSocket) {
while let Some(Ok(msg)) = socket.recv().await {
if let Message::Text(text) = msg {
// Ecoa a mensagem de texto de volta para o cliente.
let _ = socket.send(Message::Text(format!("echo: {text}"))).await;
}
}
}Quando usar isso: Clientes precisam de dados ao vivo sem polling - chat, dashboards ou streams de progresso.
Exemplo de Trabalho
use axum::{
extract::ws::{Message, WebSocket, WebSocketUpgrade},
response::sse::{Event, KeepAlive, Sse},
routing::get,
Router,
};
use futures_util::stream::{self, Stream};
use std::{convert::Infallible, time::Duration};
use tokio_stream::StreamExt as _;
async fn ws_upgrade(ws: WebSocketUpgrade) -> impl axum::response::IntoResponse {
ws.on_upgrade(handle_ws)
}
async fn handle_ws(mut socket: WebSocket) {
while let Some(Ok(msg)) = socket.recv().await {
if let Message::Text(t) = msg {
// Tenta enviar a mensagem de volta. Se falhar, encerra o loop.
if socket.send(Message::Text(format!("echo: {t}"))).await.is_err() {
break;
}
}
}
}
async fn sse_handler() -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
// Cria um stream que emite 5 eventos de "tick" com um intervalo.
let stream = stream::iter(0..5).map(|i| {
Ok(Event::default().data(format!("tick {i}")))
});
// Configura o SSE com um intervalo de keep-alive de 15 segundos.
Sse::new(stream).keep_alive(KeepAlive::new().interval(Duration::from_secs(15)))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/ws", get(ws_upgrade)) // Rota para o endpoint WebSocket
.route("/events", get(sse_handler)); // Rota para o endpoint SSE
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}O que isso demonstra:
WebSocketUpgraderealiza o handshake de upgrade HTTP.- Loop de eco sobre frames
Message::Text. Ssetransmite itensEventcom pings de keep-alive.- Ambos os endpoints coexistem em um único roteador Axum.
Mergulho Profundo
Como Funciona
- WebSockets começam como HTTP GET com
Upgrade: websocket; Axum lida com o handshake. - SSE usa
text/event-streame mensagens unidirecionais do servidor para o cliente. - Conexões de longa duração precisam de heartbeat/ping para sobreviver a proxies e balanceadores de carga.
Quando Escolher Cada Um
| Transporte | Direção | Adequação |
|---|---|---|
| WebSocket | Bidirecional | Chat, jogos, edição colaborativa |
| SSE | Servidor para cliente | Feeds ao vivo, notificações, métricas |
| Long poll | Requisição/resposta | Clientes legados sem SSE |
Notas de Rust
// Canal de broadcast para distribuir para muitos clientes WebSocket
let (tx, _) = tokio::sync::broadcast::channel::<String>(100);Armadilhas
- Sem heartbeat - Proxies fecham WebSockets ociosos após 60s. Correção: Envie periodicamente frames de ping ou comentários SSE.
- Bloqueando o loop do socket - Trabalho pesado em
handle_wsparalisa todas as mensagens. Correção:tokio::spawnpara cada tarefa de conexão com canais. - Memória ilimitada - Clientes lentos preenchem buffers internos. Correção: Use canais com limites; descarte ou desconecte clientes lentos.
- Autenticação apenas no upgrade - Token verificado uma vez e a conexão vive para sempre. Correção: Revalide periodicamente ou use tokens de curta duração.
- SSE através de proxies com buffer - Nginx pode fazer buffer de eventos. Correção: Desative o buffer com
X-Accel-Buffering: no.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Polling | Atualizações simples e raras | Requisitos de latência sub-segundo |
| gRPC streaming | Serviço a serviço | Clientes de navegador sem gRPC-Web |
tokio-tungstenite standalone | Servidores não-Axum | Já usando Axum |
FAQs
Mensagens binárias WebSocket?
Manipule Message::Binary para payloads protobuf ou comprimidos.
Reconexão SSE?
Clientes reconectam automaticamente; inclua o campo id para semântica de retomada.
Escalar WebSockets?
Sessões fixas ou pub/sub compartilhado (Redis) entre instâncias.
CORS para WebSockets?
Navegadores verificam o Origin durante o handshake - valide no manipulador de upgrade.
Backpressure?
Verifique erros de send e feche o socket quando o cliente desconectar.
TLS?
Termine no balanceador de carga; Axum vê HTTP puro atrás do proxy.
Testar WebSockets?
Use um cliente tokio-tungstenite contra um servidor de teste.
Tipo de conteúdo SSE?
Axum Sse define os cabeçalhos automaticamente.
Combinar com REST?
Mesmo roteador - REST para CRUD, /ws ou /events para atualizações ao vivo.
axum-extra WebSocket?
Axum 0.8 inclui extract::ws - nenhum crate separado é necessário.
Relacionado
- WebSockets - Clientes
tokio-tungstenite - Middleware & Tower - Timeouts para conexões longas
- Testando Serviços Web - Testes de integração
- Clientes HTTP (reqwest) - Clientes não-navegador
- Autenticação & Sessões - Protegendo upgrades
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.