Server-Sent Events auf Rust: Daten in Echtzeit senden ohne WebSocket

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

Wenn WebSocket zu viel ist, oder warum SSE wieder im Trend liegt

\n\n

Stellen Sie sich vor: Sie müssen Daten in Echtzeit vom Server an den Client senden. Wechselkurse, Benachrichtigungen über neue Nachrichten, Server-Logs, der Fortschritt einer Dateiverarbeitung. Das Erste, was einem in den Sinn kommt, ist WebSocket. Aber was, wenn Sie keinen bidirektionalen Kanal benötigen? Was, wenn der Client nur zuhört und der Server nur spricht?

\n\n

Hier kommen die Server-Sent Events (SSE) ins Spiel – ein Protokoll, das es dem Server ermöglicht, Daten über eine normale HTTP-Verbindung an den Client zu senden. Ohne überflüssige Handshakes, ohne komplexe Protokolle, ohne Kopfschmerzen mit Proxys und Load-Balancern. Und ja, mit Rust funktioniert das einfach hervorragend.

\n\n

In diesem Artikel werden wir uns ansehen, wie man SSE in Rust von Grund auf mit gängigen Frameworks zum Laufen bringt. Sie werden funktionierende Beispiele sehen, über Fallstricke erfahren und verstehen, wann SSE die beste Wahl für Sie ist.

\n\n

Was sind Server-Sent Events und warum Rust?

\n\n

SSE ist ein W3C-Standard (EventSource API), der es dem Server ermöglicht, Streaming-Daten über HTTP an den Client zu senden. Der Client verbindet sich mit einem Endpunkt, und der Server beginnt, Nachrichten im Format text/event-stream zu senden. Alles funktioniert über normales HTTP/1.1 oder HTTP/2, benötigt keine speziellen Proxys und wird von allen modernen Browsern unterstützt.

\n\n

Warum Rust? Weil Rust uns Folgendes bietet:

\n
    \n
  • Asynchronität ohne Überraschungen – Tokio und async-std ermöglichen es, Tausende von SSE-Verbindungen in einem einzigen Thread zu halten
  • \n
  • Speichersicherheit – keine Speicherlecks, keine Race-Conditions beim Multithread-Streaming
  • \n
  • Minimaler Ressourcenverbrauch – eine einzige SSE-Verbindung verbraucht nur wenige Kilobyte
  • \n
  • Ökosystem – Actix Web, Rocket, Axum – alle unterstützen SSE entweder von Haus aus oder mit minimalen Erweiterungen
  • \n
\n\n

Wie SSE funktioniert: Das Protokoll im Detail

\n\n

Der Client erstellt im Browser ein EventSource-Objekt und gibt eine URL an. Der Server antwortet mit dem Header Content-Type: text/event-stream und beginnt, Zeilen im folgenden Format zu senden:

\n\n
data: Hallo Welt!\\n\\n
\n\n

Jede Nachricht ist ein Block, in dem data: die Nutzlast enthält. Nachrichten werden durch zwei Zeilenumbrüche getrennt. Sie können JSON, Zahlen, Zeichenketten – alles Mögliche – übertragen. Zusätzlich gibt es die Felder id, event und retry.

\n\n

Hier ist ein minimales Beispiel einer SSE-Nachricht:

\n\n
id: 1\nevent: update\ndata: {"status": "processing", "progress": 42}\n\n
\n\n

Der Client in JavaScript empfängt dies als Ereignis update mit dem Feld data, das das JSON enthält.

\n\n

SSE-Server in Rust mit Actix Web schreiben

\n\n

Beginnen wir mit dem beliebtesten Framework – Actix Web. Wir installieren die Abhängigkeiten:

\n\n
[dependencies]\nactix-web = "4"\nactix-rt = "2"\ntokio = { version = "1", features = ["full"] }\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"
\n\n

Jetzt schreiben wir einen einfachen Endpunkt, der alle 2 Sekunden die aktuelle Uhrzeit sendet:

\n\n
use actix_web::{get, web, App, HttpRequest, HttpResponse, HttpServer, middleware};\nuse actix_web::rt::time;\nuse futures::stream::StreamExt;\nuse std::time::Duration;\n\n#[get("/events")]\nasync fn stream_events(req: HttpRequest) -> HttpResponse {\n    let mut rx = tokio::sync::broadcast::channel::<String>(100).1;\n\n    let stream = async_stream::stream! {\n        loop {\n            let message = format!("data: {}\\n\\n", chrono::Local::now());\n            yield Ok::<_, actix_web::Error>(\n                web::Bytes::from(message)\n            );\n            time::sleep(Duration::from_secs(2)).await;\n        }\n    };\n\n    HttpResponse::Ok()\n        .insert_header(("Content-Type", "text/event-stream"))\n        .insert_header(("Cache-Control", "no-cache"))\n        .insert_header(("Connection"                

Blogs

Buchempfehlungen