WebSocket in Rust: Erstellung von Echtzeit-Anwendungen auf fortgeschrittenem Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum Rust für WebSocket?



Moderne Echtzeitanwendungen — Chats, Handelsplattformen, Online-Spiele, Überwachungssysteme — erfordern hohe Leistung, minimale Latenz und Zuverlässigkeit. Rust wird mit seinem Ownership-System, Zero-Cost-Abstraktionen und der asynchronen Laufzeitumgebung Tokio zur idealen Wahl für den Bau skalierbarer WebSocket-Server.



In diesem Artikel gehen wir über einen einfachen Echo-Server hinaus und erkunden fortgeschrittene Techniken: Verwaltung tausender gleichzeitiger Verbindungen, Thread-Pools, Frameworks für komplexe Logik sowie Muster für horizontale Skalierung. Das Material richtet sich an Entwickler, die bereits mit den Grundlagen von Rust und asynchroner Programmierung vertraut sind.



Architektur eines Hochlast-WebSocket-Servers



1. Asynchrone Grundlage: Tokio und warp



Für Produktionsanwendungen verwenden wir die Kombination aus Tokio (asynchrone Laufzeitumgebung) und warp (ein filterbasiertes Web-Framework). Im Gegensatz zu einfachen Lösungen bietet warp flexible Routenzusammenstellung und integrierte WebSocket-Unterstützung.



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())}


Wichtige Punkte:

  • Wir verwenden broadcast::channel, um Nachrichten an alle verbundenen Clients zu senden (Pub/Sub-Muster).
  • Ein Kanal mit einem Puffer von 100 Nachrichten verhindert Blockierungen bei plötzlichen Traffic-Spitzen.
  • Der with_sender-Filter injiziert den Sender über eine Closure in den Handler.


2. Verwaltung des Verbindungsstatus



Auf fortgeschrittener Ebene reicht es nicht aus, einfach Nachrichten zu senden — Sie müssen den Lebenszyklus jeder Verbindung verwalten, Räume verfolgen, Authentifizierung und Timeouts handhaben. Wir verwenden eine HashMap mit Arc<Mutex<>> für sicheren Zugriff aus mehreren 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>(); // Client im Raum registrieren { let mut map = clients.lock().await; map.entry(room_id.clone()).or_insert_with(Vec::new).push(tx); }

// Task zum Senden von Nachrichten an den Client let send_task = tokio::spawn(async move { while let Some(msg) = rx.recv().await { if ws_tx.send(msg).await.is_err() { break; } } });

// Task zum Empfangen von Nachrichten vom Client let receive_task = tokio::spawn(async move { while let Some(Ok(msg)) = ws_rx.next().await { // Hier können Sie die eingehende Nachricht verarbeiten // Zum Beispiel JSON parsen, Berechtigungen prüfen println!("Erhalten: {:?}", msg); } });

// Auf Abschluss eines der Tasks warten

Blogs

Buchempfehlungen