Verhindern Sie, dass Ihre App in WebSocket-Nachrichten ertrinken oder einen WebSocket-Server mit Nachrichten überflutet, indem Sie Gegendruck ausüben.
Hintergrund
Das WebSocket API
Die WebSocket API bietet eine JavaScript-Schnittstelle für das WebSocket-Protokoll, Dadurch wird es möglich, eine wechselseitige Kommunikationssitzung zu öffnen, zwischen dem Browser des Nutzers und einem Server. Mit dieser API können Sie Nachrichten an einen Server senden und ereignisgesteuerte Antworten erhalten ohne den Server nach einer Antwort fragen zu müssen.
Streams API
Die Streams API ermöglicht JavaScript, programmatisch auf Streams von Datenblöcken zuzugreifen, die über das Netzwerk empfangen wurden und wie gewünscht verarbeiten. Ein wichtiges Konzept im Zusammenhang mit Streams ist Rückstand. Dies ist der Prozess, bei dem ein einzelner Stream oder eine Pipe-Kette reguliert die Lese- oder Schreibgeschwindigkeit. Wenn der Stream selbst oder ein Stream später in der Pipe-Kette noch überlastet ist und noch nicht bereit ist, weitere Blöcke zu akzeptieren, wird ein Signal rückwärts durch die Kette gesendet, um die Zustellung entsprechend zu verlangsamen.
Das Problem mit der aktuellen WebSocket API
Es ist nicht möglich, empfangene Nachrichten zu entlasten.
Bei der aktuellen WebSocket API erfolgt die Reaktion auf eine Nachricht
WebSocket.onmessage
,
Eine EventHandler
wird aufgerufen, wenn eine Nachricht vom Server empfangen wird.
Nehmen wir an, Sie haben eine Anwendung, die umfangreiche Verarbeitungsvorgänge für Daten ausführen muss.
wenn eine neue Nachricht eingeht.
Den Ablauf würden Sie wahrscheinlich
wie mit dem folgenden Code einrichten,
Und da Sie das Ergebnis des process()
-Aufrufs await
, sollten Sie gut sein, richtig?
// A heavy data crunching operation.
const process = async (data) => {
return new Promise((resolve) => {
window.setTimeout(() => {
console.log('WebSocket message processed:', data);
return resolve('done');
}, 1000);
});
};
webSocket.onmessage = async (event) => {
const data = event.data;
// Await the result of the processing step in the message handler.
await process(data);
};
Leider falsch! Das Problem mit der aktuellen WebSocket API ist, dass es keine Möglichkeit gibt, einen Rückstau zu erzeugen.
Wenn Nachrichten schneller eingehen, als die Methode process()
sie verarbeiten kann,
füllt der Renderingprozess entweder den Arbeitsspeicher
durch das Zwischenspeichern dieser Nachrichten,
reagieren nicht mehr, weil die CPU zu 100% oder beides verwendet wird.
Das Anwenden von Gegendruck auf gesendete Nachrichten ist nicht ergonomisch
Es ist möglich, eine Gegendruck auf gesendete Nachrichten anzuwenden. Dazu müssen die
WebSocket.bufferedAmount
was ineffizient und nicht-ergonomisch ist.
Dieses schreibgeschützte Attribut gibt die Anzahl der Datenbyte zurück, die in die Warteschlange gestellt wurden.
mit Aufrufen an
WebSocket.send()
,
aber noch nicht an das Netzwerk übertragen.
Dieser Wert wird auf null zurückgesetzt, sobald alle Daten in der Warteschlange gesendet wurden.
Wenn Sie jedoch weiterhin WebSocket.send()
aufrufen,
steigt er weiter an.
Was ist das WebSocketStream-API?
Die WebSocketStream API befasst sich mit dem Problem eines nicht vorhandenen oder nichtergonomischen Rückstaus indem Sie Streams in die WebSocket API integrieren. Das bedeutet, dass Rückdruck „kostenlos“ angewendet werden kann, ohne dass zusätzliche Kosten entstehen.
Empfohlene Anwendungsfälle für die WebSocketStream API
Beispiele für Websites, die diese API verwenden können:
- WebSocket-Anwendungen mit hoher Bandbreite, die Interaktivität aufrechterhalten, insbesondere bei Videos und Bildschirmfreigaben.
- Ähnlich verhält es sich mit Videoaufnahme und anderen Anwendungen, die viele Daten im Browser generieren. das auf den Server hochgeladen werden muss. Durch eine Überlastung kann der Client aufhören, Daten zu erzeugen, anstatt Daten im Speicher anzusammeln.
Aktueller Status
Schritt | Status |
---|---|
1. Erklärende Mitteilung erstellen | Abschließen |
2. Ersten Entwurf der Spezifikation erstellen | In Bearbeitung |
3. Feedback einholen und Design iterieren | In Bearbeitung |
4. Ursprungstest | Abschließen |
5. Starten | Nicht gestartet |
WebSocketStream API verwenden
Einleitendes Beispiel
Die WebSocketStream API ist versprechenbasiert, wodurch sich der Umgang damit ganz normal anfühlt
in einer modernen JavaScript-Welt.
Sie beginnen mit dem Erstellen eines neuen WebSocketStream
und übergeben ihm die URL des WebSocket-Servers.
Als Nächstes warten Sie, bis die Verbindung opened
ist,
Das führt zu einer
ReadableStream
und/oder
WritableStream
Durch Aufrufen der Methode
ReadableStream.getReader()
erhalten Sie schließlich eine
ReadableStreamDefaultReader
,
Sie können dann read()
Daten bis zum Abschluss des Streams, d. h. bis er ein Objekt der folgenden Form zurückgibt:
{value: undefined, done: true}
Entsprechend kann durch Aufrufen der Methode
WritableStream.getWriter()
erhalten Sie schließlich eine
WritableStreamDefaultWriter
,
Sie können dann write()
an die Daten gesendet werden.
const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.opened;
const reader = readable.getReader();
const writer = writable.getWriter();
while (true) {
const {value, done} = await reader.read();
if (done) {
break;
}
const result = await process(value);
await writer.write(result);
}
Rückdruck
Wie sieht es mit der versprochenen Gegendruckfunktion aus?
Wie bereits erwähnt, erhalten Sie die App „kostenlos“, es sind keine zusätzlichen Schritte erforderlich.
Wenn process()
zusätzliche Zeit in Anspruch nimmt, wird die nächste Nachricht erst verarbeitet, wenn die Pipeline bereit ist.
Gleichermaßen der Schritt WritableStreamDefaultWriter.write()
wird nur fortgesetzt, wenn dies sicher ist.
Erweiterte Beispiele
Das zweite Argument für WebSocketStream ist ein Options-Paket, das eine zukünftige Erweiterung ermöglicht.
Derzeit ist die einzige Option protocols
,
das sich genauso verhält wie der
zweites Argument für den WebSocket-Konstruktor:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
Das ausgewählte protocol
sowie das potenzielle extensions
sind Teil des Wörterbuchs
verfügbar über das WebSocketStream.opened
-Promise.
Alle Informationen über die Live-Verbindung werden durch dieses Promise bereitgestellt,
da es nicht relevant ist,
wenn die Verbindung fehlschlägt.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
Informationen über eine geschlossene WebSocketStream-Verbindung
Die Informationen, die im
WebSocket.onclose
und
WebSocket.onerror
Termine
im WebSocket API ist jetzt über das WebSocketStream.closed
-Promise verfügbar.
Das Versprechen wird im Falle eines nicht bereinigten
Schließvorgangs abgelehnt.
Andernfalls wird sie in den vom Server gesendeten Code und die Ursache aufgelöst.
Alle möglichen Statuscodes und ihre Bedeutung werden in der
Liste der CloseEvent
-Statuscodes.
const {code, reason} = await chatWSS.closed;
WebSocketStream-Verbindung schließen
Ein WebSocketStream kann mit einem
AbortController
Übergeben Sie daher ein AbortSignal
-
WebSocketStream
-Konstruktor übergeben.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
Alternativ können Sie auch die Methode WebSocketStream.close()
verwenden.
aber sein Hauptzweck besteht darin,
die Angabe des Attributs
Code
und der Grund, der an den Server gesendet wird.
wss.close({code: 4000, reason: 'Game over'});
Progressive Verbesserung und Interoperabilität
Chrome ist derzeit der einzige Browser, in dem die WebSocketStream API implementiert wird.
Für die Interoperabilität mit der klassischen WebSocket API
Es ist nicht möglich, empfangene Nachrichten zu entlasten.
Es ist möglich, eine Gegendruck auf gesendete Nachrichten anzuwenden. Dazu müssen die
WebSocket.bufferedAmount
was ineffizient und nicht-ergonomisch ist.
Funktionserkennung
So überprüfen Sie, ob die WebSocketStream API unterstützt wird:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
Demo
In unterstützten Browsern können Sie das WebSocketStream-API in Aktion im eingebetteten iFrame sehen, oder direkt über Glitch.
Feedback
Das Chrome-Team möchte mehr über Ihre Erfahrungen mit der WebSocketStream API erfahren.
Informationen zum API-Design
Funktioniert die API nicht wie erwartet? Oder fehlen Methoden oder Eigenschaften, die du zur Umsetzung deiner Idee benötigst? Haben Sie eine Frage oder einen Kommentar zum Sicherheitsmodell? Reichen Sie ein Spezifikationsproblem im entsprechenden GitHub-Repository ein. oder fügen Sie Ihre Gedanken zu einem vorhandenen Problem hinzu.
Problem mit der Implementierung melden
Haben Sie bei der Implementierung von Chrome einen Fehler gefunden?
Oder weicht die Implementierung von der Spezifikation ab?
Melde einen Fehler unter new.crbug.com.
Gib so viele Details wie möglich,
einfache Anweisungen zum Reproduzieren
und geben Sie Blink>Network>WebSockets
in das Feld Components ein.
Glitch eignet sich hervorragend, um schnell und einfach Reproduktionsfälle zu teilen.
Unterstützung für die API anzeigen
Möchten Sie die WebSocketStream API verwenden? Ihr öffentlicher Support hilft dem Chrome-Team bei der Priorisierung von Funktionen und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen.
Sende einen Tweet mit dem Hashtag an @ChromiumDev
#WebSocketStream
und teilen Sie uns mit, wo und wie Sie sie nutzen.
Nützliche Links
- Öffentliche Erläuterung
- Demo zur WebSocketStream API | Demo-Quelle für WebSocketStream API
- Tracking-Fehler
- ChromeStatus.com-Eintrag
- Blink-Komponente:
Blink>Network>WebSockets
Danksagungen
Die WebSocketStream API wurde von Adam Rice implementiert und Yutaka Hirano Hero-Image von Daan Mooij auf Unsplash (Unsplash).