Einleitung: Warum Rust der ideale Kandidat für Echtzeit ist?
\n\nStellen Sie sich folgende Situation vor: Sie schreiben einen Chat-Server, einen Börsenterminal oder ein Überwachungssystem in Rust. Benutzer möchten Aktualisierungen sofort erhalten. Niemand mag es, manuell auf „Aktualisieren“ zu klicken – das ist Schnee von gestern. Aber wie organisiert man die Datenlieferung vom Server zum Client?
\n\nLange Zeit war die einzige Methode Long Polling – ein eleganter, aber etwas „notdürftiger“ Trick. Dann kamen WebSockets und haben die Welt der Webentwicklung auf den Kopf gestellt. Und jetzt haben wir Rust – eine Sprache, die es ermöglicht, das Maximum aus beiden Ansätzen herauszuholen, ohne die Speichersicherheit zu opfern.
\n\nIn diesem Artikel vergleichen wir nicht nur Long Polling und WebSocket in Rust – wir schreiben funktionierende Beispiele, führen sie aus und schauen, was unter der Haube passiert. Wenn Sie eine Echtzeitanwendung in Rust schreiben und sich bei der Technologieauswahl unsicher sind – sind Sie hier genau richtig.
\n\nWas ist Long Polling einfach erklärt
\n\nLong Polling ist eine Technik, bei der der Client eine HTTP-Anfrage an den Server öffnet und … wartet. Der Server antwortet nicht sofort, sondern hält die Verbindung offen, bis ein neues Ereignis eintritt oder ein Timeout eintritt.
\n\nStellen Sie sich vor, Sie rufen einen Freund an und sagen: „Ruf mich an, wenn das Paket ankommt“ – und bleiben eine Stunde in der Leitung. Das ist Long Polling. Normales Polling ist, wenn Sie alle 5 Minuten zurückrufen: „Na, ist es schon da?“
\n\nImplementierung von Long Polling in Rust mit Actix-web
\n\nLassen Sie uns einen einfachen Server mit actix-web schreiben, der Long Polling verwendet. Wir haben eine Nachrichtenwarteschlange – wenn eine neue Nachricht eingeht, erhalten sie alle „Wartenden“.
\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 // Warte 30 Sekunden auf eine neue Nachricht\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\nDer Client hängt einfach an GET /poll. Wenn eine Nachricht eintrifft, antwortet der Server. Wenn 30 Sekunden lang nichts passiert, wird 204 No Content zurückgegeben, und der Client fragt erneut an.
Vorteile:
\n- \n
- Funktioniert über normales HTTP – es werden keine Proxyserver mit WebSocket-Unterstützung benötigt \n
- Einfache Integration in bestehende REST-APIs \n
- Ideal für Unternehmensnetzwerke, in denen Proxys WebSockets blockieren \n
Nachteile:
\n- \n
- Jeder „Poll“ hält eine Verbindung offen – bei 10.000 Clients sind das 10.000 offene TCP-Verbindungen \n
- Latenz: Der Client baut nach einem Timeout trotzdem wieder eine Verbindung auf \n
- Der Server verbraucht Ressourcen für die Aufrechterhaltung „hängender“ Anfragen \n
WebSocket in Rust: Wenn Geschwindigkeit gewünscht ist
\n\nWebSocket ist kein „Anruf und Warten“ mehr, sondern ein vollwertiger bidirektionaler Kanal. Server und Client können jederzeit Nachrichten austauschen, ohne den Overhead von HTTP-Headern.
\n\nIn Rust ist das WebSocket-Ökosystem reichhaltig: tokio-tungstenite für Low-Level-Arbeiten, actix-web mit integrierter Unterstützung und viele andere Bibliotheken.