Introdução: Por que Rust é o candidato ideal para tempo real?
\n\nImagine a seguinte situação: você está escrevendo um servidor de chat, um terminal de bolsa de valores ou um sistema de monitoramento em Rust. Os usuários querem receber atualizações instantaneamente. Ninguém gosta de clicar no botão "Atualizar" manualmente — isso é coisa do passado. Mas como organizar a entrega de dados do servidor para o cliente?
\n\nPor muito tempo, a única maneira era o long polling — um truque elegante, mas um pouco "gambiarra". Depois vieram os WebSockets e revolucionaram o mundo do desenvolvimento web. E agora temos o Rust — uma linguagem que permite extrair o máximo de ambas as abordagens, sem sacrificar a segurança de memória.
\n\nNeste artigo, não vamos apenas comparar long polling e WebSocket em Rust — vamos escrever exemplos funcionais, executá-los e ver o que acontece por baixo dos panos. Se você está escrevendo uma aplicação em tempo real em Rust e está em dúvida sobre qual tecnologia escolher — está no lugar certo.
\n\nO que é Long Polling em termos simples
\n\nLong polling é uma técnica na qual o cliente abre uma requisição HTTP para o servidor e... espera. O servidor não responde imediatamente, mas mantém a conexão aberta até que um novo evento ocorra ou até que o tempo limite expire.
\n\nImagine que você liga para um amigo e diz: "Me ligue quando a encomenda chegar" — e fica na linha por uma hora. Isso é long polling. O polling comum é quando você liga de volta a cada 5 minutos: "E aí, chegou?"
\n\nImplementação de Long Polling em Rust com Actix-web
\n\nVamos escrever um servidor simples em actix-web que usa long polling. Teremos uma fila de mensagens — quando uma nova mensagem aparece, todos os "esperadores" a recebem.
\n\nuse actix_web::{web, App, HttpServer, HttpRequest, HttpResponse};\nuse std::sync::Mutex;\nuse tokio::sync::broadcast;\n\nstruct AppState {\n tx: broadcast::Sender<String>,\n}\n\nasync fn long_poll_handler(data: web::Data<Mutex<AppState>>) -> HttpResponse {\n let mut rx = {\n let state = data.lock().unwrap();\n state.tx.subscribe()\n };\n\n // Aguarda uma nova mensagem por 30 segundos\n match tokio::time::timeout(\n std::time::Duration::from_secs(30),\n rx.recv()\n ).await {\n Ok(Ok(msg)) => HttpResponse::Ok().body(msg),\n Ok(Err(_)) => HttpResponse::InternalServerError().finish(),\n Err(_) => HttpResponse::NoContent().finish(), // timeout\n }\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n let (tx, _) = broadcast::channel::<String>(100);\n\n HttpServer::new(move || {\n App::new()\n .app_data(web::Data::new(Mutex::new(AppState { tx: tx.clone() })))\n .route("/poll", web::get().to(long_poll_handler))\n })\n .bind("127.0.0.1:8080")?\n .run()\n .await\n}\n\nO cliente simplesmente ficará pendurado em GET /poll. Quando uma mensagem chegar — o servidor responderá. Se não houver nada em 30 segundos — retornará 204 No Content, e o cliente fará uma nova requisição.
Vantagens:
\n- \n
- Funciona via HTTP comum — não são necessários servidores proxy com suporte a WebSocket \n
- Simplicidade de integração com APIs REST existentes \n
- Ideal para redes corporativas onde proxies bloqueiam WebSockets \n
Desvantagens:
\n- \n
- Cada "polling" mantém uma conexão — com 10.000 clientes, são 10.000 conexões TCP abertas \n
- Latência: o cliente ainda se reconecta após o timeout \n
- O servidor gasta recursos mantendo requisições "penduradas" \n
WebSocket em Rust: quando a velocidade é o que importa
\n\nWebSocket não é mais uma "ligação e espera", mas sim um canal bidirecional completo. O servidor e o cliente podem trocar mensagens a qualquer momento, sem a sobrecarga dos cabeçalhos HTTP.
\n\nNo ecossistema Rust, o WebSocket é rico: tokio-tungstenite para trabalho de baixo nível, actix-web com su