iToverDose/Software· 16 MAI 2026 · 20:01

WebSocket-Verbindungen zeigen „verbunden“ – doch die Daten bleiben aus

Eine WebSocket-Verbindung erscheint stabil, obwohl keine Daten mehr fließen. Ein häufiger Fehler in Echtzeitsystemen, der 22 Stunden unbemerkt blieb – bis die Anwendung ohne Logs oder Fehler abstürzte.

DEV Community4 min0 Kommentare

Seit Wochen nutze ich eine API, die Krypto-Signale in Echtzeit verarbeitet. Alles schien perfekt zu laufen: Die Deployment-Dashboards zeigten grüne Statusleuchten, die Logs füllten sich kontinuierlich – bis mir auffiel, dass seit fast einem Tag keine neuen Daten mehr in der Datenbank gespeichert wurden.

Keine Fehlermeldungen. Keine Abstürze. Die Anwendung lief weiter, als wäre nichts geschehen. Doch die WebSocket-Verbindung zu Binance, über die ich Echtzeit-Ticker-Daten bezog, hatte aufgehört, Nachrichten zu senden. Das Problem: Der TCP-Socket war weiterhin offen, die Verbindung galt als „verbunden“, doch die Daten flossen nicht mehr.

Dieser Fall ist kein Einzelfall. Jeder, der auf langlebige WebSocket-Verbindungen setzt – sei es für Börsenkurse, Chat-Nachrichten, IoT-Telemetrie oder Log-Streams – ist diesem stillen Ausfallrisiko ausgesetzt. Hier erfährst du, warum TCP Keepalive versagt und wie du solche Fehler zuverlässig erkennst.

Die Illusion einer „aktiven“ Verbindung

Wenn ein Client eine WebSocket-Verbindung aufbaut, durchläuft der darunterliegende TCP-Socket eine Handshake-Prozedur. Ab diesem Moment bedeutet der Status „verbunden“ lediglich, dass es einen offenen TCP-Kanal zwischen Client und Server gibt – und dass TCP den Weg für intakt hält.

Doch genau hier liegt das Problem: TCP Keepalive, sofern aktiviert, sendet zwar regelmäßig leere Pakete, um die Verbindung zu prüfen. Doch diese Mechanismen überprüfen nur, ob der Netzwerkpfad funktioniert. Sie geben keine Auskunft darüber, ob:

  • der Server weiterhin Daten sendet,
  • ein Load Balancer oder Proxy die Abonnements verwirft oder
  • ein Backend-Fehler zwar die Verbindung offenhält, aber keine Ereignisse mehr auslöst.

Deine WebSocket-Verbindung kann auf TCP-Ebene makellos wirken, während die Datenübertragung längst zum Stillstand gekommen ist.

In meinem Fall hatte die Binance-WebSocket-Schnittstelle die Verbindung akzeptiert, meine Abonnements registriert – und dann einfach aufgehört, Ticker-Updates zu senden. Der TCP-Socket blieb intakt. Das Betriebssystem meldete keine Probleme. Mein Code funktionierte einwandfrei. Doch die Daten waren weg.

Warum einfache Lösungen scheitern

Die naheliegendste Reaktion lautet: „Ich reconnecte bei Fehlern.“ Doch genau hier liegt der Haken – denn es gab keine Fehler. Keine Exceptions. Keine Verbindungsabbrüche. Die Verbindung war technisch gesehen perfekt, nur die Daten blieben aus.

Ein zweiter Ansatz: „Ich füge einen Watchdog-Timer hinzu, der den Server pinged.“ Das ist schon näher an der Lösung, hat aber seine Tücken. Viele WebSocket-Dienste – darunter die meisten Börsen-Feeds – reagieren nicht auf Client-Pings. Dein Ping geht hinaus, kommt als Echo zurück oder bleibt stumm – und du kannst nicht unterscheiden, ob der Server nicht antwortet oder ob er defekt ist.

Ein dritter Versuch: „Ich sende eine Subscribe-Nachricht und prüfe die Bestätigung.“ Das funktioniert zwar bei Initialisierungsfehlern, nicht jedoch bei Ausfällen während des Betriebs.

Die echte Lösung ist simpel:

Beobachte den Zeitpunkt der letzten empfangenen Nachricht. Überschreitet der Zeitabstand eine definierte Schwelle, ist der Datenstrom veraltet – unabhängig davon, was TCP meldet.

So baust du eine stabile Staleness-Erkennung auf

Die folgende Python-Implementierung zeigt, wie du eine WebSocket-Verbindung mit Nachrichtenebenen-Überwachung absicherst. Das Herzstück ist ein asynchroner Monitor, der regelmäßig prüft, ob neue Daten eingetroffen sind.

import asyncio
import json
import time
import websockets

STALENESS_TIMEOUT_SECONDS = 60  # Anpassbar an die erwartete Nachrichtenfrequenz

class StaleStreamError(Exception):
    pass

async def consume_stream(url, subscribe_message):
    while True:
        try:
            async with websockets.connect(url) as ws:
                await ws.send(json.dumps(subscribe_message))
                last_message_at = time.time()

                async def monitor_staleness():
                    while True:
                        await asyncio.sleep(STALENESS_TIMEOUT_SECONDS)
                        age = time.time() - last_message_at
                        if age > STALENESS_TIMEOUT_SECONDS:
                            await ws.close()
                            raise StaleStreamError(
                                f"Keine Nachricht seit {age:.1f}s "
                                f"(Schwelle: {STALENESS_TIMEOUT_SECONDS}s)"
                            )

                staleness_task = asyncio.create_task(monitor_staleness())
                try:
                    async for message in ws:
                        last_message_at = time.time()
                        await handle_message(message)
                finally:
                    staleness_task.cancel()
        
        except websockets.exceptions.ConnectionClosed:
            print("Verbindung geschlossen, Reconnect-Versuch...")
        except StaleStreamError as e:
            print(f"Veraltete Daten erkannt: {e} – Reconnect-Versuch...")
            await asyncio.sleep(1)  # Backoff vor dem erneuten Verbindungsaufbau

async def handle_message(message):
    # Hier die Verarbeitung der empfangenen Nachricht
    pass

Der entscheidende Gedanke: Definiere „Lebendigkeit“ nicht auf Betriebssystem-Ebene, sondern auf Anwendungsebene. Dein Feed könnte natürliche Ruhephasen haben – etwa bei geschlossenem Börsenhandel oder geringem IoT-Traffic. Passe die Timeout-Schwelle entsprechend an.

Ein guter Richtwert: Setze deine Timeout-Grenze auf das 3- bis 5-fache des erwarteten maximalen Nachrichtenabstands während der langsamsten Phase.

Reicht ein Heartbeat?

Einige WebSocket-Protokolle integrieren explizite Heartbeats – kleine, periodische Nachrichten, die bestätigen, dass beide Seiten aktiv sind. Binance Futures etwa sendet alle paar Minuten ein Ping, auf das du mit einem Pong antworten musst.

Solche Mechanismen sind hilfreich, lösen das Staleness-Problem jedoch nicht vollständig:

  • Heartbeats funktionieren möglicherweise weiter, während die Daten-Abonnierung fehlschlägt (unterschiedliche Code-Pfade auf Serverseite),
  • manche Feeds bieten gar keine Heartbeats an oder
  • selbst mit Heartbeats brauchst du eine eigene Logik, um den Datenstrom zu überwachen.

Behandle Heartbeats als zusätzliche Informationsquelle, nicht als einzige Wahrheit. Die entscheidende Frage lautet: „Erhalte ich die Art von Nachrichten, für die ich mich angemeldet habe?“

Reconnect-Logik: Effizient und belastbar gestalten

Wenn du eine veraltete Datenverbindung erkennst und einen Reconnect einleitest, solltest du folgende Aspekte beachten:

  • Exponentieller Backoff: Bei einem echten Server-Ausfall vermeidest du eine Flut von Reconnect-Versuchen, die das System zusätzlich belasten.
  • Jitter: Treten 1.000 Clients gleichzeitig in den Reconnect-Modus (etwa nach einem Server-Ausfall), verhindert eine zufällige Verzögerung zwischen den Versuchen eine Überlastung.
  • Zustandswiederherstellung: Bei zustandsbehafteten Feeds (z. B. Orderbücher oder Abonnement-Kanäle) kann ein Reconnect erfordern, den aktuellen Zustand neu abzufragen.
  • Alerting: Erfolgen mehr als N Reconnects innerhalb von M Minuten, deutet dies auf ein tieferliegendes Problem hin – hier sollte eine Benachrichtigung an das Team ausgelöst werden.

Die 22-Stunden-Lektion

Der Fehler, der mich traf, war kein subtiles Problem. Er ist ein bekanntes Risiko in Systemen mit langlebigen Streaming-Verbindungen. Doch ich hatte meine Anwendung unter der Annahme gebaut, dass eine „WebSocket-Verbindung = Datenfluss“ bedeutet – und diese Annahme brach zusammen, als sie keine Gültigkeit mehr hatte.

Was das Problem nachhaltig behoben hat:

  • Nachrichtenebenen-Überwachung (das oben beschriebene Muster),
  • Externe Gesundheitschecks – ein Endpunkt, der das Alter der letzten empfangenen Nachricht zurückgibt, sodass Tools wie UptimeRobot bei Überschreiten der Schwelle alarmieren können.

Langfristig gilt: Vertraue nicht auf die TCP-Schicht, um die Integrität deiner Datenpipeline zu gewährleisten. Baue stattdessen Mechanismen ein, die direkt auf die Anwendungsebene zielen. Denn am Ende zählt nicht, ob die Verbindung „verbunden“ ist – sondern ob die Daten fließen, die du brauchst.

KI-Zusammenfassung

WebSocket 'bağlandı' diyor ama veri gelmiyorsa sorun TCP keepalive'de değil. Uygulama katmanında donukluk algılama nasıl yapılır? Pratik Python örneğiyle açıklanıyor.

Kommentare

00
KOMMENTAR SCHREIBEN
ID #7UZZCK

0 / 1200 ZEICHEN

Menschen-Check

3 + 9 = ?

Erscheint nach redaktioneller Prüfung

Moderation · Spam-Schutz aktiv

Noch keine Kommentare. Sei der erste.