Introdução: por que Rust para WebSocket?
Aplicativos modernos em tempo real — chats, terminais de ações, jogos online, sistemas de monitoramento — exigem alta performance, latência mínima e confiabilidade. Rust, com seu sistema de ownership, abstrações de custo zero e runtime assíncrono Tokio, torna-se a escolha ideal para construir servidores WebSocket escaláveis.
Neste artigo, iremos além de um simples servidor echo e exploraremos técnicas avançadas: gerenciamento de milhares de conexões simultâneas, pools de threads, frameworks para construção de lógica complexa e padrões para escalabilidade horizontal. O material é voltado para desenvolvedores já familiarizados com os fundamentos de Rust e programação assíncrona.
Arquitetura de um servidor WebSocket de alta carga
1. Base assíncrona: Tokio e warp
Para nível de produção, usamos a combinação de Tokio (runtime assíncrono) e warp (framework web baseado em filtros). Diferente de soluções simples, o warp oferece composição flexível de rotas e suporte nativo a WebSocket.
use warp::Filter;use futures_util::{SinkExt, StreamExt};use tokio::sync::broadcast;
#[tokio::main]async fn main() { let (tx, _rx) = broadcast::channel::<String>(100); let ws_route = warp::path("ws") .and(warp::ws()) .and(with_sender(tx.clone())) .map(|ws: warp::ws::Ws, tx| { ws.on_upgrade(move |socket| handle_connection(socket, tx)) });
let health_route = warp::path("health") .map(|| warp::reply::json(&serde_json::json!({"status": "ok"})));
let routes = ws_route.or(health_route); warp::serve(routes).run(([0, 0, 0, 0], 8080)).await;}
fn with_sender(tx: broadcast::Sender<String>) -> impl Filter<Extract = (broadcast::Sender<String>,), Error = std::convert::Infallible> + Clone { warp::any().map(move || tx.clone())}Pontos-chave:
- Usamos
broadcast::channelpara distribuir mensagens a todos os clientes conectados (padrão "pub/sub"). - O canal com buffer de 100 mensagens previne bloqueios durante picos repentinos de tráfego.
- O filtro
with_senderinjeta o remetente no handler através de um closure.
2. Gerenciamento de estado das conexões
Em nível avançado, não basta apenas enviar mensagens — é necessário gerenciar o ciclo de vida de cada conexão, rastrear salas, autenticação e timeouts. Usamos HashMap com Arc<Mutex<>> para acesso seguro a partir de múltiplas tasks.
use std::collections::HashMap;use std::sync::Arc;use tokio::sync::Mutex;use warp::ws::Message;
type Clients = Arc<Mutex<HashMap<String, Vec<tokio::sync::mpsc::UnboundedSender<Message>>>>>;
async fn handle_connection(ws: warp::ws::WebSocket, clients: Clients, room_id: String) { let (mut ws_tx, mut ws_rx) = ws.split(); let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Message>(); // Registra o cliente na sala { let mut map = clients.lock().await; map.entry(room_id.clone()).or_insert_with(Vec::new).push(tx); }
// Task para enviar mensagens ao cliente let send_task = tokio::spawn(async move { while let Some(msg) = rx.recv().await { if ws_tx.send(msg).await.is_err() { break; } } });
// Task para receber mensagens do cliente let receive_task = tokio::spawn(async move { while let Some(Ok(msg)) = ws_rx.next().await { // Aqui pode processar a mensagem recebida // Por exemplo, fazer parse de JSON, verificar permissões println!("Recebido: {:?}", msg); } });
// Aguarda a conclusão de qualquer uma
```