Zum Inhalt springen
zurück zu den projekten

// projekt ·

Verteiltes Echtzeit-Chatsystem

Ein Chat, um zu lernen, was kaputtgeht, wenn aus einem Server viele werden. Stack: Go, WebSocket, Redis, Docker.

Go / WebSocket / Redis Pub/Sub / Docker / React

Rolle: Privatprojekt · Jahr: 2024 - 2025 · Status: ausgeliefert

kurz: Ein Chatsystem, gebaut um zu untersuchen, was kaputtgeht, wenn WebSocket-Verkehr über mehrere Server verteilt leben muss. Goroutines halten die Verbindungen, Redis Pub/Sub verteilt Nachrichten zwischen den Knoten, ein kleiner eigener Load Balancer sitzt davor. Das Interessante ist nicht der Chat, sondern was aufhört zu funktionieren, wenn aus einem Knoten zwei werden.

Problem

Ein Chatserver auf einem Knoten hält jede Verbindung im Speicher, und das funktioniert. Mit einem zweiten Knoten zerfällt der State: Ein Nutzer auf Server A erreicht keinen Nutzer auf Server B, weil A keinen Zugriff auf die Sockets von B hat. Verbindungsverwaltung und Nachrichtenverteilung müssen getrennt werden.

Rahmenbedingungen

  • Sockets können nicht umziehen. Ein WebSocket ist eine langlebige TCP-Verbindung, gebunden an einen Prozess. Umziehen heißt Verbindungsabbruch.
  • Speicher pro Knoten ist knapp. Keine schwere Framework-Schicht zwischen Socket und Chatlogik.
  • Reihenfolge und Übersicht. Nachrichten müssen in Reihenfolge ankommen, und das System muss wissen, wer wo verbunden ist, während Knoten dazukommen und wegfallen.

Entscheidungen

  • Go für die Server. Goroutines halten untätige Verbindungen billig und vermeiden Callback-Event-Loops.
  • Redis Pub/Sub als Bus. Knoten synchronisieren keine Client-Listen. Eine Nachricht, die auf Server A ankommt, wird auf einem Kanal veröffentlicht, Server B empfängt sie und schreibt sie an seine lokalen Clients.
  • Docker für die Topologie, damit der Schritt von einem auf mehrere Knoten lokal wiederholbar ist.

Ergebnis

In horizontalen Skalierungstests hielt jeder Knoten 500+ gleichzeitige Verbindungen, mit untätigen Goroutines unter 10 KB. Der wichtigste Kompromiss ist die Zustellung. Redis Pub/Sub ist at-most-once: Ein Knoten, der gerade neu startet, wenn eine Nachricht veröffentlicht wird, sieht sie nie. Für eine Studie über Fan-out habe ich das akzeptiert und Eventual Consistency statt verteilter Sperren gewählt. Dauerhafte Zustellung hieße Umstieg auf Redis Streams oder ein Log mit Consumer-Offsets.