3. Docker mit Websocket¶
3.1. Einleitung¶
Im zweiten Versuch des DVA-Praktikums wurde die bestehende Containerarchitektur aus dem ersten Versuch erweitert, um eine Webanwendung mit interaktiver Kommunikation umzusetzen. Kernfunktion dieser Anwendung ist die Kommunikation mittels WebSocket.
Im Gegensatz zum ersten Versuch, der rein auf HTTP-Anfragen basierte, kommt hier nun die bidirektionale Kommunikation über WebSockets zum Einsatz. Dabei wird sowohl das Frontend als auch das Backend in Rust entwickelt, was eine homogene, performante und moderne Architektur ermöglicht.
3.2. WebSocket-Kommunikation¶
3.2.1. Was ist ein WebSocket?¶
Ein WebSocket ist ein Netzwerkprotokoll, das eine voll-duplexfähige Kommunikation zwischen Client und Server ermöglicht. Im Gegensatz zu HTTP, das auf dem Request-Response-Modell basiert, erlaubt WebSocket eine ständige Verbindung, über die beide Parteien Nachrichten senden können, wann immer sie möchten.
Typische Vorteile:
Geringe Latenz durch konstante Verbindung
Kein Overhead durch ständige HTTP-Requests
Ideal für Echtzeitanwendungen wie Chats, Spiele oder – wie in unserem Fall – Live-Wett-Anwendungen
3.2.2. Ablauf der Verbindung¶
Der Client baut eine Verbindung zum Server über einen HTTP-Upgrade-Request auf.
Sobald der Server den Upgrade akzeptiert, wird die Verbindung auf das WebSocket-Protokoll umgestellt.
Danach können Nachrichten in beide Richtungen asynchron gesendet und empfangen werden.
Die Verbindung bleibt offen, bis sie explizit durch eine der Parteien beendet wird.
3.2.3. Integration in die Anwendung (Rust / Actix)¶
Im Backend wird actix-web zusammen mit actix-web-actors verwendet, um WebSocket-Handler als sogenannte Actors zu realisieren. Das bedeutet: jede aktive Verbindung (Session) läuft isoliert in einem eigenen Task, der Nachrichten empfangen und senden kann.
Wichtige Komponenten:
SessionManager: verwaltet alle aktiven Verbindungen und bietet Broadcast-Funktionalitäten.
WebSocketSession: implementiert die Logik pro Client-Verbindung, inklusive Empfang und Verarbeitung von Nachrichten.
ws_index: Einstiegspunkt der WebSocket-Verbindung (/ws/), an den sich Clients initial verbinden.
Ausschnitt WebSocketSession:
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketSession {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
if let Ok(ws::Message::Text(text)) = msg {
// Verarbeitung der Nachricht
println!("Nachricht vom Client: {:?}", text);
// Weiterleiten an alle verbundenen Clients
self.session_manager.lock().unwrap().broadcast(text.clone());
}
}
}
Diese Architektur erlaubt es:
Nachrichten von einem Client direkt an alle anderen zu verteilen (z. B. eine Wette)
Antworten oder Reaktionen auf diese Nachricht in Echtzeit zu erfassen
Die Verbindung während der gesamten Interaktion offen zu halten
3.2.4. Sicherheitsaspekte und Zustandsverwaltung¶
Da WebSocket-Verbindungen dauerhaft sind, muss das Backend sicherstellen:
dass Verbindungen verwaltet, abgebaut und ggf. wiederhergestellt werden können
dass der Zustand zwischen mehreren Clients konsistent bleibt (z. B. wer hat auf was gewettet?)
dass kein unautorisierter Zugriff erfolgt (z. B. durch Sitzungsverwaltung oder Tokens – im Projekt über SessionManager gelöst)
3.2.5. Fazit¶
Die Nutzung von WebSockets stellt einen klaren Mehrwert für interaktive, latenzsensible Anwendungen dar. Gerade im Kontext von Echtzeitfunktionen wie Wetten, Spielständen oder Benutzereingaben ist diese Technologie essenziell und zeigt hier ihre Stärken. Durch die Kombination mit Rust und Actix ergibt sich eine leistungsfähige und zugleich schlanke Serverarchitektur mit moderner Event-Logik.
3.3. Architektur der Anwendung¶
Die Applikation besteht aus zwei Containern:
Backend: Verarbeitet die WebSocket-Kommunikation, verwaltet aktive Verbindungen und verarbeitet Wetten.
Frontend: Ein Rust-Frontend, das direkt im Browser läuft.
3.3.1. Backend: WebSocket-Server mit Actix¶
Das Backend basiert auf dem Webframework actix-web und nutzt actix-websockets für die Echtzeitkommunikation. Die zentrale Einstiegspunkt der Anwendung liegt in der main.rs. [actix.rs]
Codeblock der main.rs-Datei:
mod ws;
mod routes;
mod utils;
use actix_web::{App, HttpServer, web};
use actix_cors::Cors;
use std::sync::{Arc, Mutex};
use ws::session::SessionManager;
use routes::{ws_route::ws_index, image::get_picture};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let sessions = Arc::new(Mutex::new(SessionManager::new()));
HttpServer::new(move || {
let cors = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header()
.max_age(3600);
App::new()
.wrap(cors)
.app_data(web::Data::new(sessions.clone()))
.route("/bild", web::get().to(get_picture))
.route("/ws/", web::get().to(ws_index))
})
.bind("0.0.0.0:8080")?
.run()
.await
}
Erklärung:
mod ws, mod routes, mod utils importieren interne Module.
Der SessionManager verwaltet die aktiven WebSocket-Sitzungen. Er wird via Arc<Mutex<…>> threadsicher im gesamten Server geteilt.
CORS wird aktiviert, um Zugriffe von verschiedenen Ursprüngen (z. B. vom Frontend-Container) zu erlauben.
Zwei Endpunkte werden definiert: - GET /ws/: startet die WebSocket-Verbindung (ws_index) - GET /bild: liefert ein Bild zurück (nicht primärer Teil der Kommunikation, aber exemplarisch für weitere API-Endpunkte)
Der WebSocket-Endpunkt /ws/ wird über routes/ws_route.rs definiert und verwendet eine Funktion ws_index. Hier wird für jede neue Verbindung eine eigene WebSocketSession instanziiert, welche wiederum mit dem zentralen SessionManager verbunden ist. Der Vorteil: Alle Sitzungen können untereinander Nachrichten austauschen oder zentral gesteuert werden.
Dies erlaubt eine flexible Echtzeit-Kommunikation.
3.4. Docker-Setup mit Compose¶
Wie im ersten Versuch wurde auch hier eine docker-compose.yaml-Datei zur Orchestrierung der Container verwendet
3.5. Zusammenfassung¶
Im zweiten Versuch wurde die bestehende Containerstruktur um eine echtzeitfähige Webanwendung erweitert. Durch den Einsatz von WebSockets konnte eine bidirektionale Kommunikation zwischen Frontend und Backend realisiert werden. Das ermöglicht neue Anwendungsfälle wie Live-Wetten, Spiele oder dynamische Updates – und das mit minimaler Latenz.
Die Verwendung von Rust für Frontend und Backend bringt Vorteile in Performance, Sicherheit und Code-Konsistenz.
3.6. Literaturangaben¶
Actix-Web Dokumentation – WebSockets https://actix.rs/docs/websockets/ (besucht am 19.04.2025)
WebSocket.org – Einführung in WebSockets https://www.websocket.org/ (besucht am 19.04.2025)
WebSocket vs. HTTP Kommunikationsprotokolle https://sendbird.com/de/developer/tutorials/websocket-vs-http-communication-protocols (besucht am 19.04.2025)