WebSocket en Rust : création d'applications temps réel de niveau avancé

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi Rust pour WebSocket ?



Les applications temps réel modernes — chats, terminaux boursiers, jeux en ligne, systèmes de surveillance — exigent des performances élevées, des latences minimales et une fiabilité. Rust, avec son système de possession, ses abstractions à coût zéro et son runtime asynchrone Tokio, devient le choix idéal pour construire des serveurs WebSocket évolutifs.



Dans cet article, nous dépasserons le simple serveur d'écho et examinerons des techniques avancées : gestion de milliers de connexions simultanées, pools de threads, frameworks pour construire une logique complexe, ainsi que des modèles pour la mise à l'échelle horizontale. Le contenu est destiné aux développeurs déjà familiers avec les bases de Rust et la programmation asynchrone.



Architecture d'un serveur WebSocket à forte charge



1. Base asynchrone : Tokio et warp



Pour un niveau production, nous utilisons la combinaison Tokio (runtime asynchrone) et warp (framework web basé sur des filtres). Contrairement aux solutions simples, warp offre une composition flexible des routes et un support intégré de 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())}


Points clés :

  • Nous utilisons broadcast::channel pour diffuser des messages à tous les clients connectés (modèle "pub/sub").
  • Un canal avec un tampon de 100 messages empêche les blocages en cas de pic soudain de trafic.
  • Le filtre with_sender injecte l'expéditeur dans le gestionnaire via une fermeture.


2. Gestion de l'état des connexions



Au niveau avancé, il ne suffit pas d'envoyer des messages — il faut gérer le cycle de vie de chaque connexion, suivre les salles, l'authentification et les délais d'attente. Nous utilisons HashMap avec Arc<Mutex<>> pour un accès sécurisé depuis plusieurs tâches.



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>(); // Enregistrer le client dans la salle { let mut map = clients.lock().await; map.entry(room_id.clone()).or_insert_with(Vec::new).push(tx); }

// Tâche d'envoi de messages au client let send_task = tokio::spawn(async move { while let Some(msg) = rx.recv().await { if ws_tx.send(msg).await.is_err() { break; } } });

// Tâche de réception de messages du client let receive_task = tokio::spawn(async move { while let Some(Ok(msg)) = ws_rx.next().await { // Ici, on peut traiter le message entrant // Par exemple, analyser le JSON, vérifier les droits println!("Reçu : {:?}", msg); } });

// Attendre la fin de l'une des

Blogs

Book Recommendations