Impedisci all'app di essere annegata nei messaggi WebSocket o di inondare un server WebSocket di messaggi applicando una contropressione.
Sfondo
L'API WebSocket
L'API WebSocket fornisce un'interfaccia JavaScript al protocollo WebSocket, che consente di aprire una sessione di comunicazione interattiva bidirezionale tra il browser dell'utente e un server. Con questa API, puoi inviare messaggi a un server e ricevere risposte basate su eventi senza interrogare il server per ricevere una risposta.
API Streams
L'API Streams Consente a JavaScript di accedere in modo programmatico ai flussi di blocchi di dati ricevuti sulla rete ed elaborarle come desiderato. Un concetto importante nel contesto degli stream è rappresentato contropressione. Questo è il processo mediante il quale un singolo flusso o una catena di tubi regola la velocità di lettura o scrittura. Quando il flusso stesso o uno stream successivo nella catena delle pipeline è ancora occupato e non è ancora pronto ad accettare altri chunk, ma invia un segnale all'indietro attraverso la catena per rallentare la consegna, a seconda dei casi.
Il problema con l'API WebSocket attuale
Impossibile applicare la contropressione ai messaggi ricevuti
Con l'API WebSocket attuale, le reazioni a un messaggio avvengono in
WebSocket.onmessage
,
EventHandler
chiamato alla ricezione di un messaggio dal server.
Supponiamo che tu abbia un'applicazione che deve eseguire operazioni di analisi dei dati pesanti
ogni volta che viene ricevuto un nuovo messaggio.
Probabilmente configureresti il flusso in modo simile al codice seguente,
e siccome hai await
il risultato della chiamata process()
dovrebbe andare bene, giusto?
// 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);
};
Sbagliato! Il problema con l'API WebSocket attuale è che non è possibile applicare una contropressione.
Quando i messaggi arrivano più velocemente di quanto sia in grado di gestire il metodo process()
,
il processo di rendering riempie la memoria
con il buffering di questi messaggi,
non rispondono per il 100% di utilizzo della CPU o per entrambi.
L'applicazione della contropressione ai messaggi inviati non è ergonomica
È possibile applicare una contropressione ai messaggi inviati, ma occorre eseguire il polling della
WebSocket.bufferedAmount
che è inefficiente e non ergonomica.
Questa proprietà di sola lettura restituisce il numero di byte di dati che sono stati messi in coda
utilizzando le chiamate a
WebSocket.send()
,
ma non ancora trasmessi alla rete.
Questo valore viene reimpostato su zero una volta inviati
tutti i dati in coda
ma se continui a chiamare WebSocket.send()
,
continuerà a salire.
Che cos'è l'API WebSocketStream?
L'API WebSocketStream risolve il problema della contropressione inesistente o non ergonomica. integrando i flussi con l'API WebSocket. Ciò significa che la contropressione può essere applicata "senza costi", senza costi aggiuntivi.
Casi d'uso suggeriti per l'API WebSocketStream
Esempi di siti che possono utilizzare questa API includono:
- Applicazioni WebSocket a larghezza di banda elevata che devono mantenere l'interattività, in particolare la condivisione di video e schermate.
- Analogamente, l'acquisizione video e altre applicazioni che generano molti dati nel browser che deve essere caricato sul server. Con la contropressione, il client può smettere di produrre dati invece di accumulare dati in memoria.
Stato attuale
Passaggio | Stato |
---|---|
1. Crea messaggio esplicativo | Completato |
2. Crea la bozza iniziale delle specifiche | In corso |
3. Raccogli feedback e esegui l'iterazione del design | In corso |
4. Prova dell'origine | Completato |
5. Lancio | Non avviato |
Come utilizzare l'API WebSocketStream
Esempio introduttivo
L'API WebSocketStream è basata sulla promessa, il che rende naturale la gestione
in un mondo JavaScript moderno.
Inizierai costruendo un nuovo WebSocketStream
e passandogli l'URL del server WebSocket.
Ora attendi che la connessione arrivi a opened
,
il che comporta
ReadableStream
e/o un
WritableStream
.
Chiamando il metodo
ReadableStream.getReader()
, ottieni infine un
ReadableStreamDefaultReader
,
che potrai poi read()
i dati da quando il flusso è terminato, cioè finché non restituisce un oggetto nel formato
{value: undefined, done: true}
.
Di conseguenza, richiamando il
WritableStream.getWriter()
, ottieni infine un
WritableStreamDefaultWriter
,
che potrai poi write()
a cui inviare i dati.
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);
}
Contropressione
E la caratteristica di contropressione promessa?
Come ho scritto sopra, l'operazione è "senza costi" e non richiede ulteriori passaggi.
Se process()
richiede più tempo, il messaggio successivo verrà utilizzato solo quando la pipeline è pronta.
Allo stesso modo, il passaggio WritableStreamDefaultWriter.write()
procederà solo se è sicuro farlo.
Esempi avanzati
Il secondo argomento per WebSocketStream è un pacchetto di opzioni per consentire estensioni future.
Al momento l'unica opzione è protocols
,
che si comporta allo stesso modo
secondo argomento al costruttore WebSocket:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
La protocol
selezionata e le potenziali extensions
fanno parte del dizionario
disponibili tramite la promessa WebSocketStream.opened
.
Tutte le informazioni sulla connessione dal vivo sono fornite da questa promessa,
poiché non è pertinente se la connessione non riesce.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
Informazioni sulla connessione WebSocketStream chiusa
Le informazioni disponibili nel
WebSocket.onclose
e
WebSocket.onerror
eventi
nell'API WebSocket è ora disponibile tramite la promessa WebSocketStream.closed
.
La promessa viene rifiutata in caso di chiusura sporca,
altrimenti si risolve nel codice e nella motivazione inviati dal server.
Tutti i codici di stato possibili e il loro significato sono spiegati nel
elenco di codici di stato CloseEvent
.
const {code, reason} = await chatWSS.closed;
Chiusura di una connessione WebSocketStream
Un WebSocketStream può essere chiuso con
AbortController
Di conseguenza, trasmetti un valore AbortSignal
al costruttore WebSocketStream
.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
In alternativa, puoi anche utilizzare il metodo WebSocketStream.close()
,
ma il suo scopo principale è consentire di specificare
codice
e il motivo che viene inviato al server.
wss.close({code: 4000, reason: 'Game over'});
Miglioramento progressivo e interoperabilità
Chrome è attualmente l'unico browser a implementare l'API WebSocketStream.
Per l'interoperabilità con l'API WebSocket classica,
non è possibile applicare la contropressione ai messaggi ricevuti.
È possibile applicare una contropressione ai messaggi inviati, ma occorre eseguire il polling della
WebSocket.bufferedAmount
che è inefficiente e non ergonomica.
Rilevamento delle caratteristiche
Per verificare se l'API WebSocketStream è supportata, utilizza:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
Demo
Sui browser che supportano, puoi vedere l'API WebSocketStream in azione nell'iframe incorporato, o direttamente su Glitch.
Feedback
Il team di Chrome vuole conoscere la tua esperienza con l'API WebSocketStream.
Parlaci della progettazione dell'API
C'è qualcosa nell'API che non funziona come previsto? Oppure mancano metodi o proprietà che ti servono per implementare la tua idea? Hai una domanda o un commento sul modello di sicurezza? Segnala un problema relativo alle specifiche sul repository GitHub corrispondente o aggiungi le tue opinioni a un problema esistente.
Segnalare un problema con l'implementazione
Hai trovato un bug nell'implementazione di Chrome?
Oppure l'implementazione è diversa dalle specifiche?
Segnala un bug all'indirizzo new.crbug.com.
Includi il maggior numero di dettagli possibile, semplici istruzioni per la riproduzione,
e inserisci Blink>Network>WebSockets
nella casella Componenti.
Glitch è perfetto per condividere cover di riproduzione semplici e veloci.
Mostra il supporto per l'API
Intendi utilizzare l'API WebSocketStream? Il tuo supporto pubblico aiuta il team di Chrome a dare priorità alle funzionalità e mostra agli altri fornitori di browser quanto sia fondamentale supportarli.
Invia un tweet a @ChromiumDev utilizzando l'hashtag
#WebSocketStream
:
e facci sapere dove e come lo utilizzi.
Link utili
- Spiegazione pubblica
- Demo dell'API WebSocketStream | Origine demo dell'API WebSocketStream
- Monitoraggio del bug
- Voce ChromeStatus.com
- Componente Blink:
Blink>Network>WebSockets
Ringraziamenti
L'API WebSocketStream è stata implementata da Adam Rice e Yutaka Hirano. Immagine hero di Daan Mooij su Rimuovi schermo.