4 Punkte von GN⁺ 2024-03-19 | 1 Kommentare | Auf WhatsApp teilen
  • Bei Echtzeit-Web-Apps unterscheiden sich Latenz, Bidirektionalität, Implementierungsaufwand und betriebliche Einschränkungen stark danach, ob man sich für Long Polling, WebSockets, SSE, WebRTC oder WebTransport zur Übertragung von Server-Client-Ereignissen entscheidet
  • WebSockets bieten bidirektionale Kommunikation über eine einzelne langlebige Verbindung, in der Praxis werden wegen Erkennung von Verbindungsverlusten, Wiederverbindung und Ping-Pong-Heartbeats aber oft Bibliotheken wie Socket.IO zusätzlich eingesetzt
  • Server-Sent Events sind ein HTTP-basiertes unidirektionales Streaming vom Server zum Client, wodurch Implementierung und Wiederverbindungslogik einfacher werden, die native EventSource-API hat jedoch Einschränkungen beim Senden von POST-Bodies oder benutzerdefinierten Headern
  • WebTransport unterstützt auf Basis von HTTP/3 QUIC mehrere Streams sowie zuverlässige und unzuverlässige Übertragung, ist aber Stand März 2024 ein Working Draft und wegen fehlender nativer Unterstützung in Safari und Node.js noch schwer als universelle Option zu sehen
  • Wegen Beendigung im mobilen Hintergrund, Begrenzung der Verbindungszahl pro Domain, Unternehmens-Proxys und Firewalls sowie verlorener Ereignisse während Wiederverbindungen braucht man in echten Apps zusätzlich Synchronisations-Wiederherstellungslogik und Infrastrukturtests

Der Wandel der Echtzeit-Kommunikation zwischen Server und Client

  • In Echtzeit-Webanwendungen ist die Fähigkeit des Servers, Ereignisse an den Client zu senden, zu einer zentralen Anforderung geworden
  • Anfangs wurde Long Polling, das über HTTP läuft, im Browser als Methode für Server-Client-Messaging verwendet
  • Später kamen WebSockets als robustere Methode für bidirektionale Kommunikation hinzu
  • Server-Sent Events (SSE) bieten unidirektionale Kommunikation vom Server zum Client auf einfachere Weise
  • WebTransport könnte eine effizientere, flexiblere und besser skalierbare Methode werden, die Unterstützung ist derzeit jedoch noch eingeschränkt
  • WebRTC kann für einige Nischenfälle von Server-Client-Ereignissen in Betracht gezogen werden, verfolgt aber einen anderen Zweck und wird daher nicht als Hauptoption behandelt

Long Polling

  • Long Polling ist eine Methode, die Server-Push-Kommunikation mit gewöhnlichen XHR-Anfragen nachbildet
  • Wenn der Client eine Anfrage an den Server offen hält, verzögert der Server die Antwort, bis neue Daten verfügbar sind
  • Nachdem neue Informationen gesendet wurden, wird die Verbindung geschlossen, und der Client startet sofort die nächste Anfrage
  • Im Vergleich zu klassischem periodischem Polling sind Updates schneller möglich, und unnötiger Netzwerkverkehr sowie Serverlast lassen sich reduzieren
  • Allerdings ist die Methode weniger effizient als Echtzeit-Techniken wie WebSockets, und je nach Zeitpunkt der Datenübertragung kann zusätzliche Latenz entstehen
  • Die Client-Implementierung ist einfach, im Backend ist es jedoch schwer sicherzustellen, dass Clients während einer Wiederverbindung keine Ereignisse verpassen

WebSockets

  • WebSockets erzeugen eine einzelne langlebige Verbindung zwischen Client und Server und ermöglichen Vollduplex-Kommunikation (full-duplex)
  • Sobald die Verbindung aufgebaut ist, können beide Seiten unabhängig voneinander Daten senden, ohne den Overhead des HTTP-Request-Response-Zyklus
  • Sie eignen sich für Apps mit niedriger Latenz und häufigen Updates, etwa Echtzeit-Chat, Spiele oder Finanzhandelsplattformen
  • Die grundlegende WebSocket-API ist leicht zu verwenden, in der Produktion wird der Umgang mit Verbindungsverlusten und Neuaufbau jedoch komplex
  • Da sich schwer erkennen lässt, ob eine Verbindung noch nutzbar ist, ergänzt man üblicherweise Ping-and-Pong-Heartbeats
  • Wegen dieser Komplexität werden oft Bibliotheken wie Socket.IO verwendet, die bei Bedarf auch ein Long-Polling-Fallback bereitstellen

Server-Sent Events

  • Server-Sent Events (SSE) sind ein standardisierter Weg, Server-Updates über HTTP an den Client zu pushen
  • Anders als WebSockets sind sie ausschließlich für unidirektionale Kommunikation vom Server zum Client ausgelegt
  • Sie eignen sich für Live-Newsfeeds, Sportergebnisse oder Echtzeit-Updates, bei denen der Client keine Nachrichten an den Server senden muss
  • SSE lässt sich als eine einzelne HTTP-Anfrage verstehen, bei der die Verbindung offen bleibt und das Backend bei jedem Ereignis die Antwort zeilenweise weiterstreamt
  • Im Browser empfängt der Client den Ereignisstrom durch Initialisierung einer EventSource-Instanz
  • EventSource verbindet sich im Gegensatz zu WebSockets bei Verbindungsabbrüchen automatisch neu
  • Der Server muss den Content-Type-Header auf text/event-stream setzen und Felder wie Event-Typ, Daten-Payload, Event-ID und Retry-Timing gemäß der SSE specification formatieren

WebTransport

  • WebTransport ist eine API für effiziente Kommunikation mit niedriger Latenz zwischen Web-Client und Server
  • Sie nutzt das HTTP/3 QUIC protocol, um Daten über mehrere Streams zu senden
  • Unterstützt werden sowohl zuverlässige als auch unzuverlässige Übertragung sowie ungeordnete Datenübermittlung
  • Für Apps mit Hochleistungs-Networking wie Echtzeitspiele, Live-Streaming oder Kollaborationsplattformen kann das ein starkes Werkzeug sein
  • Stand März 2024 befindet sich WebTransport im Status Working Draft und wird noch nicht breit unterstützt
  • Im Safari browser ist es noch nicht verfügbar, und auch in Node.js fehlt native Unterstützung
  • Selbst bei breiterer Unterstützung ist die API sehr komplex, daher ist es wahrscheinlich, dass eher Bibliotheken auf WebTransport aufbauen, statt dass es direkt im Anwendungscode genutzt wird

WebRTC

  • WebRTC ist ein Open-Source-Projekt und API-Standard, der Echtzeitkommunikation in Browsern und mobilen Apps ohne Plugins ermöglicht
  • Es unterstützt Peer-to-Peer-Verbindungen für Audio-, Video- und Datenaustausch zwischen Browsern
  • Um NATs und Firewalls zu durchqueren, werden Protokolle wie ICE, STUN und TURN verwendet
  • WebRTC wurde zwar für Client-zu-Client-Interaktionen entwickelt, kann aber auch für Server-Client-Kommunikation genutzt werden, wenn ein Server sich wie ein Client verhält
  • Diese Art der Nutzung passt nur zu Nischenanwendungen und wird deshalb aus dem Hauptvergleich ausgeschlossen
  • Damit WebRTC funktioniert, ist ohnehin ein Signaling-Server nötig, der wiederum über WebSockets, SSE oder WebTransport betrieben wird
  • Deshalb ist WebRTC als direkter Ersatz für diese Technologien nur begrenzt sinnvoll

Wichtige Einschränkungen je Technologie

  • Bidirektionale Datenübertragung

    • Nur WebSockets und WebTransport unterstützen das Empfangen von Serverdaten und das Senden von Clientdaten über dieselbe Verbindung
    • Long Polling ist theoretisch ebenfalls möglich, wird aber nicht empfohlen, weil zum Senden neuer Daten über eine bestehende Long-Polling-Verbindung zusätzliche HTTP-Anfragen nötig sind
    • Bei Long Polling ist es besser, Client→Server-Daten über separate HTTP-Anfragen zu senden, ohne die bestehende Verbindung zu stören
    • SSE unterstützt kein Senden zusätzlicher Daten an den Server
    • Die native EventSource API kann selbst bei der initialen Anfrage keine Daten wie bei einem POST im HTTP-Body mitsenden
    • Die Daten müssen in URL-Parametern stecken, was aus Sicherheitsgründen problematisch ist, weil Credentials in Server-Logs, Proxys oder Caches auftauchen können
    • RxDB verwendet deshalb statt der nativen EventSource API das eventsource polyfill, das Funktionen wie benutzerdefinierte HTTP-Header ergänzt
    • Microsofts fetch-event-source erlaubt das Senden von Body-Daten und die Verwendung von POST statt GET
  • Begrenzung der Verbindungszahl pro Domain

    • Die meisten modernen Browser erlauben 6 Verbindungen pro Domain, was die Nutzbarkeit stabiler Server→Client-Messaging-Methoden insgesamt einschränkt
    • Diese Grenze von 6 Verbindungen wird auch zwischen Browser-Tabs geteilt, sodass mehrere offene Tabs derselben Seite denselben Verbindungspool gemeinsam nutzen müssen
    • Das HTTP/1.1-RFC empfiehlt sogar nur 2 Verbindungen pro Server oder Proxy
    • Diese Richtlinie ist sinnvoll, um DDoS über Besucher zu verhindern, kann aber problematisch werden, wenn legitime Server-Client-Kommunikation mehrere Verbindungen benötigt
    • Als Umgehung kann man HTTP/2 oder HTTP/3 verwenden, sodass der Browser nur eine einzelne Verbindung pro Domain öffnet und Daten per Multiplexing verarbeitet
    • Auch bei HTTP/2 und HTTP/3 begrenzt die Einstellung SETTINGS_MAX_CONCURRENT_STREAMS die tatsächliche Zahl gleichzeitiger Streams; in den meisten Konfigurationen liegt der Standard bei 100 concurrent streams
    • Für bestimmte APIs wie EventSource könnten Browser die Verbindungslimits zwar erhöhen, doch entsprechende Issues bei Chromium und Firefox sind als „won’t fix“ markiert
  • Weniger Verbindungen in Browser-Apps

    • In Browser-Apps sollte man davon ausgehen, dass Nutzer die Anwendung gleichzeitig in mehreren Tabs öffnen können
    • Standardmäßig könnte jeder Tab eine eigene Server-Stream-Verbindung öffnen, meistens ist das aber unnötig
    • Es ist möglich, auch bei mehreren geöffneten Tabs nur eine einzige Verbindung aufzubauen und diese zwischen den Tabs zu teilen
    • RxDB nutzt dafür broadcast-channel npm package mit LeaderElection, um nur einen einzigen Replikations-Stream zwischen Server und Client aufrechtzuerhalten
    • Das Paket kann auch ohne RxDB eigenständig in anderen Anwendungen verwendet werden

Betriebliche Einschränkungen durch Mobilgeräte, Proxys und Firewalls

  • In mobilen Betriebssystemen wie Android und iOS ist es schwer, offene Verbindungen wie WebSockets dauerhaft aufrechtzuerhalten
  • Mobile Betriebssysteme können Apps nach einer gewissen Zeit der Inaktivität in den Hintergrund verschieben und offene Verbindungen schließen
  • Dieses Verhalten ist Teil von Ressourcenstrategien zur Batterieschonung und Performance-Optimierung
  • Entwickler setzen daher häufig mobile Push-Benachrichtigungen statt dauerhafter Verbindungen ein, wenn der Server Daten an den Client senden soll
  • Push-Benachrichtigungen ermöglichen es dem Server, die App auch ohne permanent offene Verbindung über neue Daten zu informieren und ihr Verhalten oder Updates anzustoßen
  • In Unternehmensumgebungen können Proxys und Firewalls Nicht-HTTP-Verbindungen blockieren, wodurch sich WebSocket-Server schwer in die Infrastruktur integrieren lassen
  • In solchen Umgebungen kann das HTTP-basierte SSE leichter in Unternehmenssysteme eingebunden werden
  • Auch Long Polling bleibt eine mögliche Option, da es nur gewöhnliche HTTP-Anfragen verwendet

Leistungsvergleich

  • Beim Vergleich von WebSockets, SSE, Long Polling und WebTransport sollte man Latenz, Durchsatz, Serverlast und Skalierbarkeit gemeinsam betrachten
  • Das realtime-web repo, das Nachrichtenzeiten mit einer Go-Serverimplementierung testet, zeigt ähnliche Leistungswerte für WebSockets, WebRTC und WebTransport
  • Da WebTransport eine neue Technik auf Basis von HTTP/3 ist, könnten nach März 2024 weitere Performance-Optimierungen hinzukommen
  • WebTransport ist darauf optimiert, den Energieverbrauch zu senken, dieser Wert wurde in den Tests jedoch nicht gemessen
  • Latenz

    • WebSockets bieten wegen Vollduplex-Kommunikation über eine einzelne dauerhafte Verbindung die geringste Latenz
    • SSE bietet ebenfalls geringe Latenz bei Server→Client-Kommunikation, für Nachrichten vom Client an den Server ist jedoch eine zusätzliche HTTP-Anfrage nötig
    • Long Polling hat höhere Latenz, weil für jede Datenübertragung eine neue HTTP-Verbindung aufgebaut wird
    • Bei Long Polling kann die Latenz stark ansteigen, wenn der Server ein Ereignis senden will, während der Client gerade eine neue Verbindung öffnet
    • WebTransport wird mit ähnlich geringer Latenz wie WebSockets erwartet und profitiert von effizienterem Multiplexing und Congestion Control in HTTP/3
  • Durchsatz

    • WebSockets können dank dauerhafter Verbindung hohen Durchsatz liefern, allerdings kann das Problem von backpressure den Durchsatz beeinträchtigen, wenn der Client die Server-Senderate nicht schnell genug verarbeiten kann
    • SSE hat weniger Overhead als WebSockets und kann bei unidirektionalen Server→Client-Broadcasts potenziell höheren Durchsatz erreichen
    • Long Polling liefert wegen des Overheads durch häufiges Öffnen und Schließen von Verbindungen generell geringeren Durchsatz und verbraucht mehr Serverressourcen
    • WebTransport wird voraussichtlich hohen Durchsatz sowohl für unidirektionale als auch bidirektionale Streams innerhalb einer einzelnen Verbindung unterstützen und könnte in Szenarien mit mehreren Streams WebSockets übertreffen
  • Skalierbarkeit und Serverlast

    • WebSockets können die Serverlast mit steigender Zahl gleichzeitiger Verbindungen deutlich erhöhen, was die Skalierbarkeit bei Apps mit vielen Nutzern beeinträchtigen kann
    • SSE ist in Szenarien mit vorwiegend Server→Client-Updates besser skalierbar
    • SSE verwendet normale HTTP-Anfragen ohne WebSocket-spezifische Verfahren wie protocol upgrade, wodurch der Verbindungs-Overhead geringer ist
    • Long Polling ist wegen häufiger Verbindungsaufbauten am wenigsten skalierbar und eignet sich eher nur als Fallback-Mechanismus
    • WebTransport wurde mit Blick auf hohe Skalierbarkeit und effiziente Verbindungs- und Stream-Verarbeitung in HTTP/3 konzipiert und könnte die Serverlast gegenüber WebSockets und SSE senken

Empfehlungen nach Anwendungsfall

  • SSE ist die am einfachsten verständliche Option in der Implementierung, nutzt bestehende HTTP/S-Protokolle und vermeidet dadurch leichter Einschränkungen durch Unternehmens-Firewalls sowie technische Probleme anderer Protokolle
  • Es lässt sich leicht in Node.js und andere Server-Frameworks integrieren
  • Es eignet sich für Apps mit häufigen Server→Client-Updates wie Newsfeeds, Aktienkurse oder Live-Event-Streaming
  • WebSockets sind stark in Szenarien mit dauerhafter bidirektionaler Kommunikation
  • Für Browser-Spiele, Chat-Anwendungen oder Live-Sport-Updates mit kontinuierlicher Interaktion sind sie die erste Wahl
  • WebTransport hat Potenzial, aber die Unterstützung in Server-Frameworks ist noch nicht breit, und die Kompatibilität mit Node.js und Safari ist unzureichend
  • WebTransport hängt von HTTP/3 ab, und die HTTP/3-Unterstützung vieler Webserver wie nginx ist noch experimental
  • Es ist eine zukunftsorientierte Technologie mit Unterstützung für zuverlässige und unzuverlässige Datenübertragung, für die meisten aktuellen Anwendungsfälle aber noch keine praktikable Option
  • Long Polling ist wegen der Ineffizienz wiederholter neuer HTTP-Verbindungen und des hohen Overheads im Allgemeinen eine veraltete Methode
  • In Umgebungen ohne Unterstützung für WebSockets oder SSE kann es als Fallback dienen, für den allgemeinen Einsatz wird es wegen der Performance-Einschränkungen jedoch nicht empfohlen

Problem verlorener Ereignisse während Wiederverbindungen

  • Wenn man Funktionen auf einer Echtzeit-Streaming-Technologie aufbaut, müssen Verbindungsabbrüche und Wiederverbindungen immer mitgedacht werden
  • Ist der Client gerade verbunden, verbindet sich neu oder ist offline, kann er Ereignisse verpassen, die auf dem Server entstehen
  • Wenn der Server wie bei Aktienkursen jedes Mal den vollständigen Zustand streamt, sind verlorene Ereignisse möglicherweise nicht kritisch
  • Streamt das Backend jedoch nur Teilergebnisse, müssen verlorene Ereignisse unbedingt behandelt werden
  • Es ist nicht gut skalierbar, wenn das Backend sich für jeden Client merken muss, welche Ereignisse erfolgreich übertragen wurden
  • Besser ist es, dieses Problem mit Logik auf der Client-Seite zu lösen
  • Die RxDB Sync Engine verwendet dafür zwei Betriebsmodi
    • checkpoint iteration mode: Das Backend wird wiederholt über normale HTTP-Anfragen abgefragt, bis der Client beim erneuten Synchronisieren aufgeholt hat
    • event observation mode: Updates aus dem Echtzeit-Stream halten den Client synchron
  • Wenn die Client-Verbindung abbricht oder ein Fehler auftritt, wechselt die Replikation vorübergehend in den checkpoint iteration mode, bis wieder derselbe Zustand wie auf dem Server erreicht ist
  • Dieser Ansatz gleicht verpasste Ereignisse aus und sorgt dafür, dass der Client stets exakt mit dem Server synchron bleiben kann

Was in Unternehmensinfrastrukturen geprüft werden sollte

  • In Unternehmensinfrastrukturen können bei allen Streaming-Technologien Probleme auftreten
  • Proxys und Firewalls können Traffic blockieren oder Requests und Responses unbeabsichtigt beschädigen
  • Wenn man in solchen Umgebungen Echtzeit-Apps implementiert, sollte man zuerst testen, ob die gewählte Technologie in der vorhandenen Infrastruktur überhaupt funktioniert

1 Kommentare

 
GN⁺ 2024-03-19
Hacker-News-Kommentare
  • Server-Sent Events mochte ich schon immer. Einfach und leicht zu schreiben/implementieren.

    • Mit IPv6 ist das inzwischen leicht vollständig skalierbar, und wenn man es sauber aufbaut, kann es fast zustandslos funktionieren, indem man dem Client nur eine Liste von SSE-Diensten gibt, was die Skalierung deutlich einfacher macht.
      WebSockets werden ab einem gewissen Nutzungsniveau bei der Skalierung ziemlich komplex.
    • Dank der Einfachheit lässt es sich über ein CDN viel leichter skalieren als WebSockets: https://www.fastly.com/blog/server-sent-events-fastly
    • Stimme zu. Allerdings ist man auf 6 SSE-Streams pro Origin und Browser-Instanz beschränkt, sodass ohne zusätzliche clientseitige Komplexität bei 6 Tabs Schluss sein kann.
      https://crbug.com/275955
    • Der Nachteil ist, dass man die Payload als base64 kodieren oder Zeilenumbrüche entfernen muss.
      Ich frage mich, warum man daraus nicht einfach eine multipart-Streaming-Antwort gemacht hat. Das würde auch Metadaten unterstützen und ist ein sehr häufig implementiertes Format.
    • Funktioniert sogar mit gewöhnlichem Apache prefork und PHP.
  • Darüber hinaus gibt es noch weitere Nachteile, die man kennen sollte.
    WebSockets haben weder Flow Control (Backpressure) noch Multiplexing, man muss das also bei Bedarf selbst bauen oder etwas wie RSocket verwenden. SSE kann ebenfalls keine Binärdaten direkt senden und braucht daher eine Kodierung wie base64.
    WebTransport geht diese Probleme an und löst auch HOL-Blocking, aber ich frage mich, ob es am Ende nicht wie bei Python 2→3 oder der Umstellung auf IPv6 dazu kommt, dass Leute bei den bestehenden Versionen bleiben, weil der wahrgenommene Nutzen eines Upgrades gering ist.
    Solange Browser weiter über TCP funktionieren, können einige Netzwerke UDP und damit HTTP/3/WebTransport komplett blockieren.

    • HOL-Blocking ist ein Problem, aber TCP bietet Flow Control. Wenn man das nicht nutzen will, geht man eben auf HTTP/3.
      Die Sorge, dass der Umstieg auf WebTransport langsam sein könnte, hätte man früher genauso bei TLS-Transport, HTTP/3 oder XHR äußern können. Da ein paar große Browser-Engines dominieren, ist die Auslieferung neuer Browser-Features und Protokolle vergleichsweise einfach.
      Die Logik „TCP geht, also können manche Netzwerke UDP blockieren“ ist ähnlich wie zu sagen, dass HTTP 1.1 ohne TLS möglich ist und deshalb HTTP/2 und TLS weiter blockiert würden. Ganz falsch ist das nicht, aber wenn man sich die breite Einführung von HTTP/2 und besonders TLS ansieht, scheint es doch kein so großes Problem zu sein.
    • Ich höre ständig von Netzwerken, die UDP blockieren, habe das aber noch nie in der Praxis gesehen. Viele Dinge laufen über UDP.
      In kleinen Büros oder dystopischen Unternehmensumgebungen wie im Film kann man das vielleicht abschalten, aber ich verstehe nicht, warum die Tatsache, dass einige Netzwerke UDP verbieten könnten, hier so relevant sein soll. Manche Netzwerke blockieren auch google.com oder wikipedia.com, und trotzdem scheitern diese Dienste nicht.
    • WebSocketStream kommt bald in Chrome und bringt Backpressure mit: https://chromestatus.com/feature/5189728691290112
    • Als besserer Ansatz als base64 gibt es binary-sse: https://github.com/luciopaiva/binary-sse
    • In so einer Situation würde HTTP/2 immer noch funktionieren, daher ist Multiplexing dann weniger problematisch.
  • Die Erklärung zu WebRTC im Artikel ist nicht korrekt. Client/Server-WebRTC ist auch ohne separaten „Signaling-Server“ möglich, denn der Server kann das Signaling selbst übernehmen.
    Es braucht nur ein paar zusätzliche Roundtrips, aber keinen separaten Server. WebRTC-Datenkanäle funktionieren ziemlich gut als Ersatz für WebSockets oder SSE, besonders wenn man HOL-Blocking vermeiden möchte. Es gibt auch viele Bibliotheken wie Pion oder str0m, die fast die ganze Arbeit übernehmen.
    Auch die Aussage, die WebTransport-API sei komplex, wirkt übertrieben. Wenn man die erweiterten Funktionen nicht braucht, kann man sie ignorieren, und wenn man es wie WebSocket nutzen will, reicht es fast schon, einen bidirektionalen Stream zu öffnen. Um HOL-Blocking zu vermeiden, kann man pro Nachricht einen Stream öffnen. Das ist etwas komplexer, aber nicht auf einem Niveau, bei dem man zwingend eine Bibliothek bräuchte, und GitHub Copilot schreibt den Code wahrscheinlich sogar für einen. Allerdings ist WebTransport noch in der Reifephase, daher gibt es noch nicht viele Server-Bibliotheken, und auf Safari-Support wartet man ebenfalls noch.

    • Dass Client/Server-WebRTC ohne „Signaling-Server“ möglich sein soll, überrascht mich.
      Üblicherweise wird ein Signaling-Server mit WebSocket implementiert. Wenn nicht ein dezentrales Bootstraping bestehender Clients gemeint ist, lässt sich das nicht mit WebRTC selbst umsetzen.
  • Wenn man für Kunden mit traditioneller „Enterprise“- und „Security“-IT-Infrastruktur entwickelt, ist es wahrscheinlich besser, einfach einen Aktualisieren-Button einzubauen und es dabei zu belassen.
    Solche Versuche, für genau solche Kunden Echtzeitfunktionen zu bauen, sind meiner Erfahrung nach in diesen Umgebungen zuverlässig gescheitert und wegen endloser Prozesse anschließend nicht einmal zu beheben.

    • Jetty/CometD fällt auf Long Polling zurück, wenn andere Transportwege nicht funktionieren.
    • Ehrlich gesagt haben all diese Technologien ihre jeweils eigenen Probleme, und der Aktualisieren-Button ist da keine Ausnahme.
    • Im Browser gibt es zwar einen Aktualisieren-Button, aber wahrscheinlich zerstört ein Klick darauf die Anwendung ohnehin
  • WebSocket und SSE werden in großem Maßstab zu einem erheblichen Wartungsproblem. Besonders im Backend braucht man eigene Observability, und im Frontend auf Mobilgeräten wird das Debugging zum Albtraum, wenn man es nicht extrem vorsichtig implementiert
    Geräte schalten aus Energiespargründen das Netzwerk ab oder drosseln es, erst recht, wenn man nicht explizit über dedizierte APIs I/O ausführt
    Das Erzeugen neuer Verbindungen ist teuer, und der Server muss den Zustand irgendwo speichern. Wenn diese zustandsbehaftete Speicherschicht Probleme hat, versuchen Clients immer weiter neu zu verbinden, laufen in Timeouts und hängen ewig an teuren Operationen fest. Es ist auch keine gute Methode, den Durchsatz einfach zu steuern und die Datenbank langsam zu belasten
    Was die Zuverlässigkeit angeht, war Long Polling meiner Erfahrung nach am besten. Selbst wenn ein ereignisbasierter Ablauf wirklich wichtig ist, ist eine zweistufige Architektur besser: Das Frontend macht Long Polling zu einem Backend der ersten Ebene, und diese erste Ebene abonniert per WebSocket ein Backend der zweiten Ebene; so lässt sich die Zuverlässigkeit viel besser kontrollieren

    • Stimme voll zu. Ich habe gesehen, wie man sich mit WebSocket und SSE selbst ins Knie schießt. Long Polling ist zwar kostspielig, aber meiner Meinung nach der am besten erklärbare und am besten skalierbare Ansatz
    • SSE unterstützt Long Polling. Man kann den Server die Verbindung schließen lassen, wenn er möchte
      SSE unterstützt automatische Wiederverbindung und übermittelt auch die zuletzt gesehene ID, sodass der Server nahtlos fortsetzen kann
    • Im verlinkten Artikel werden viele dieser Punkte behandelt, und rxdb hat Mechanismen, um mehrere dieser Bedenken abzumildern
  • Im Artikel fehlt zwar Short Polling, aber es gehört ebenfalls dazu. Es ist keine Methode, mit der der Server Nachrichten an den Client sendet, aber wenn es keine andere Wahl gibt, etwa bei Shared Hosting, ist es immer noch nützlich
    Meiner Erfahrung nach funktioniert es ziemlich gut, selbst bei langen Polling-Intervallen, zum Beispiel 20 Sekunden, wenn man jeder Antwort eine Nachrichtenliste beilegt. Wenn der Nutzer einen Button drückt, sendet der Client eine Anfrage an den Server, und der Server antwortet mit den Daten zusammen mit der aktuellen Nachrichtenliste, sodass der Client wieder auf dem neuesten Stand ist

    • Das kann auch bei sich schnell ändernden Daten funktionieren. Besonders dann, wenn der Anteil der Polling-Anfragen, die Updates enthalten, hoch ist
  • Ich verstehe immer noch nicht, warum WebSocket und SSE bei der initialen Anfrage nicht das Senden von Headern wie Authorization unterstützen. Damit wird die gesamte Authentifizierung für Echtzeitdienste dem Implementierer überlassen
    Vielleicht gibt es in der Spezifikation einen guten Weg, aber ich habe so viele unterschiedliche Ansätze gesehen, dass man inzwischen fast sagen kann, dass es faktisch keinen gibt

    • Die EventSource API hat einiges, was frustrierend ist. Ich bin Maintainer des am häufigsten verwendeten EventSource-Polyfills, habe aber vor Kurzem ein Projekt gestartet, das den EventSource-Client modern neu aufsetzt: https://github.com/rexxars/eventsource-client
      Es unterstützt nicht nur benutzerdefinierte Header, sondern auch alle Request-Methoden (POST, PATCH usw.), inklusive Request-Body, benannte Event-Subscriptions und das initiale Setzen der last event ID. Man kann es auch als asynchronen Iterator verwenden
      Ich mag die Einfachheit von Server-Sent Events, aber die EventSource-API wirkt, als wäre sie hastig implementiert und dann einfach so liegen gelassen worden
      [1]: https://github.com/eventsource/eventsource
    • Kann die initiale Anfrage nicht ohnehin den vollständigen Satz standardmäßiger HTTP-Header und sogar Cookies mitsenden?
    • Cookies werden gesendet
    • TLS-Zertifikate gibt es auch
    • Ich benutze das seit Jahren, übersehe ich etwas?
  • Vielleicht ist das naiv, aber unter der Annahme von HTTP/2 oder höher wirkt eine Kombination aus EventSource und fetch() für das Senden von Nachrichten fast so gut wie andere Protokolle, die eine einzelne TCP-Verbindung nutzen. HTTP/3 verwendet UDP, also noch besser
    Vorausgesetzt natürlich, dass die Verbindung nur gehalten werden muss, solange der Tab im Vordergrund ist. Mich würde interessieren, auf welche Probleme man in der Praxis gestoßen ist, wenn man das wirklich versucht hat

    • Eine Einschränkung ist, dass SSE nur Text unterstützt und man Binärdaten daher nicht effizient senden kann. Man braucht eine Kodierung wie base64
    • Es gibt eine Bibliothek, die genau das macht
      https://www.npmjs.com/package/@microsoft/fetch-event-source
    • Ich hatte genau denselben Gedanken. Mit HTTP/2 und SSE müssten doch 99 % der Probleme gelöst sein, dachte ich
      Statt etwas völlig anderes zu machen, habe ich mich gefragt, ob man SSE nicht weiter ausreizen könnte, während man Latenz, Speicherverbrauch und CPU-Ressourcen weiter senkt
    • Wenn der hauptsächliche Anwendungsfall Server → Client ist, dann ja
  • Solche Artikel finde ich irgendwie interessant. Ich habe Ende der 90er ein Online-Auktionssystem entworfen, und es gab überhaupt keine XHR-Anfragen
    Alle Echtzeit-Updates wurden per Server-Push/HTTP-Streaming abgewickelt. Damals war es nicht leicht, all diese offenen Verbindungen zu handhaben, aber mit der richtigen Architektur war es bis zu einer akzeptablen Größenordnung machbar

    • Ich habe wirklich viel Zeit damit verbracht, Leuten die Bedeutung von HTTP-Streaming zu erklären, und es ist definitiv kein leichter Kampf
      Die Vorteile von HTTP/2 oder HTTP/3 sind großartig, aber man sollte auch wissen, was sich mit dem praktisch überall unterstützten HTTP 1.1 erreichen lässt
  • Ich vermisse Long Polling ein bisschen. Im Vergleich zu modernen Technologien war es wirklich simpel. Das finde ich sogar aus der Perspektive, dass WebRTC das Beste ist.

    • SSE ist nicht wirklich komplexer als Long Polling. Der Unterschied ist nur, dass der Server die Verbindung nicht direkt nach dem Senden einer Antwort schließt
      Stattdessen wartet er erneut auf Daten und sendet weitere Antworten über denselben Stream.
    • Schön wäre es, wenn es so simpel wäre, aber das ist es nicht.
      Das Networking von Second Life verwendet Long-Polling-HTTPS für einen „Event-Kanal“, und der Server sendet Ereignisnachrichten über diesen Kanal an den Client. Die meisten Nachrichten gehen über UDP, aber wenn Verschlüsselung nötig ist oder Nachrichten groß sind, gehen sie über den HTTPS/TCP-Event-Kanal.
      Der C++-Client auf der Client-Seite verwendet libcurl, aber die standardmäßigen Timeout-Einstellungen passen nicht zu Long Polling. libcurl trennt die Verbindung und erstellt eine neue Anfrage, wodurch Nachrichten verloren gehen oder doppelt auftreten können.
      Auf der Server-Seite steht Apache vor dem eigentlichen Simulationsserver und filtert irrelevante Verbindungsversuche heraus, aber auch Apache hat eigene Timeouts, unterbricht Verbindungen und löst erneute Verbindungsversuche des Clients aus.
      Man versucht, Verluste mit Nachrichtensequenznummern zu verhindern, aber die Second-Life-Server ignorieren die Sequenznummern, die der Client zur Bestätigung zurücksendet. Einige kompatible Server von Open Simulator überspringen sogar Sequenznummern.
      Das Ergebnis ist ein HTTPS-basiertes System, in dem Nachrichten, die eigentlich zuverlässig sein sollten, verloren gehen oder dupliziert werden können. Wenn einige dieser Nachrichten verloren gehen, kommt die Benutzeraktivität im Spiel zum Stillstand.
      Die Leute, die das entworfen haben, sind schon lange weg, und die heutigen Mitarbeiter wussten nicht, wie gravierend dieses Chaos ist. Externe Nutzer mussten die Probleme finden und dokumentieren, und die Mitarbeiter des Unternehmens versuchen seit Monaten, sie zu beheben. Es ist offenbar schwer genug zu reparieren, dass man die Arbeit daran derzeit eher aufschiebt.
      Deshalb ist Long Polling nicht „simpel bis zur Dummheit“. Der richtige Weg ist vermutlich, häufig genug Keep-Alive-Nachrichten zu senden, damit die TCP- und HTTPS-Schichten nicht in ein Timeout laufen. Dann bleibt man in einem Pfad, auf dem Apache und libcurl korrekt funktionieren.
    • Es wird immer noch oft verwendet. Für viele Anwendungen ist es sinnvoll, den Overhead zusätzlicher Requests in Kauf zu nehmen und dafür alles im bestehenden Kontext einer HTTP API zu halten.
    • Auch heute kann man Long Polling noch mit HTTP/2 verwenden, und es wird nicht verschwinden.