Empêchez votre application de se noyer dans les messages WebSocket ou d'inonder un serveur WebSocket de messages en appliquant une contre-pression.
Contexte
API WebSocket
L'API WebSocket fournit une interface JavaScript au protocole WebSocket, ce qui permet d'ouvrir une session de communication interactive dans les deux sens entre le navigateur de l'utilisateur et un serveur. Avec cette API, vous pouvez envoyer des messages à un serveur et recevoir des réponses basées sur des événements sans interroger le serveur pour obtenir une réponse.
API Streams
L'API Streams permet à JavaScript d'accéder de manière programmatique aux flux de fragments de données reçus sur le réseau et les traiter comme vous le souhaitez. Dans le contexte des flux, le concept contre-pression. Il s'agit du processus par lequel un seul flux ou une chaîne régule la vitesse de lecture ou d'écriture. Lorsque le flux lui-même ou un flux ultérieur dans la chaîne du pipeline est toujours occupé et qu'elle n'est pas encore prête à accepter plus de fragments, il envoie un signal en arrière à travers la chaîne pour ralentir la livraison le cas échéant.
Le problème avec l'API WebSocket actuelle
Il est impossible d'appliquer une contre-pression aux messages reçus
Avec l'API WebSocket actuelle, la réaction à un message s'effectue dans
WebSocket.onmessage
,
un EventHandler
appelé lorsqu'un message est reçu du serveur.
Supposons que vous ayez une application qui doit effectuer des opérations intensives de traitement de données
chaque fois qu'un nouveau message est reçu.
Vous configurerez probablement le flux
comme le code ci-dessous,
Et comme vous utilisez await
pour obtenir le résultat de l'appel process()
, tout devrait fonctionner correctement, n'est-ce pas ?
// 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);
};
Faux ! Le problème avec l'API WebSocket actuelle est qu'il n'y a aucun moyen d'appliquer une contre-pression.
Lorsque les messages arrivent plus rapidement que la méthode process()
ne peut les gérer,
le processus de rendu remplira la mémoire
en mettant en mémoire tampon ces messages,
ne répondent plus à cause d'une utilisation du processeur de 100 %, ou les deux.
L'application d'une contre-pression aux messages envoyés n'est pas ergonomique
Il est possible d'appliquer une contre-pression aux messages envoyés, mais vous devez interroger
WebSocket.bufferedAmount
ce qui est inefficace et non ergonomique.
Cette propriété en lecture seule renvoie le nombre d'octets de données mis en file d'attente
à l'aide d'appels vers
WebSocket.send()
,
mais pas encore transmises au réseau.
Cette valeur est réinitialisée une fois que
toutes les données en file d’attente ont été envoyées,
mais si vous appelez WebSocket.send()
sans cesse,
il va continuer à grimper.
Qu'est-ce que l'API WebSocketStream ?
L'API WebSocketStream résout le problème de la contre-pression inexistante ou non ergonomique en intégrant des flux à l'API WebSocket. Cela signifie que la contre-pression peut être appliquée "sans frais", sans frais supplémentaires.
Cas d'utilisation suggérés pour l'API WebSocketStream
Voici quelques exemples de sites qui peuvent utiliser cette API:
- Applications WebSocket à haut débit qui doivent conserver l'interactivité en particulier pour la vidéo et le partage d'écran.
- De même, les applications de capture vidéo et autres qui génèrent beaucoup de données dans le navigateur à importer sur le serveur. Avec la contre-pression, le client peut arrêter de produire des données au lieu de les accumuler en mémoire.
État actuel
Étape | État |
---|---|
1. Créer une vidéo explicative | Fin |
2. Créer l'ébauche initiale de la spécification | En cours |
3. Recueillir des commentaires et itérer sur la conception | En cours |
4. Phase d'évaluation | Fin |
5. Lancer | Non démarré |
Utiliser l'API WebSocketStream
Exemple d'introduction
L'API WebSocketStream est basée sur des promesses, ce qui rend son traitement naturel
dans un monde JavaScript moderne.
Commencez par construire un nouveau WebSocketStream
et transmettez-lui l'URL du serveur WebSocket.
Ensuite, vous attendez que la connexion soit opened
.
ce qui entraîne un
ReadableStream
et/ou un
WritableStream
En appelant la méthode
ReadableStream.getReader()
vous obtenez enfin une
ReadableStreamDefaultReader
,
Vous pouvez ensuite read()
données jusqu'à la fin du flux, c'est-à-dire jusqu'au renvoi d'un objet au format
{value: undefined, done: true}
Par conséquent, en appelant la méthode
WritableStream.getWriter()
vous obtenez enfin une
WritableStreamDefaultWriter
,
Vous pouvez ensuite write()
les données.
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);
}
Contre-pression
Qu'en est-il de la fonctionnalité de contre-pression annoncée ?
Comme je vous l'ai dit plus haut, vous bénéficiez d'un accès "sans frais" et ne nécessite aucune autre action de votre part.
Si la fonction process()
prend plus de temps, le message suivant n'est consommé que lorsque le pipeline est prêt.
De même, l'étape WritableStreamDefaultWriter.write()
ne poursuivra que si cela ne présente aucun risque.
Exemples avancés
Le deuxième argument de WebSocketStream est un sac d'options qui permet une extension future.
Actuellement, la seule option est protocols
,
qui se comporte de la même manière que
deuxième argument du constructeur WebSocket:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
L'élément protocol
sélectionné et les extensions
potentiels font partie du dictionnaire
disponible via la promesse WebSocketStream.opened
.
Toutes les informations concernant la connexion en direct sont fournies par cette promesse.
car ce n'est pas pertinent si la connexion échoue.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
Informations sur la connexion WebSocketStream fermée
Les informations disponibles dans le
WebSocket.onclose
et
WebSocket.onerror
événements
de l'API WebSocket est désormais disponible via la promesse WebSocketStream.closed
.
La promesse rejette en cas
de clôture impropre,
sinon il se résout au code et au motif
envoyés par le serveur.
Tous les codes d'état possibles et leur signification sont expliqués dans le
Liste des codes d'état CloseEvent
.
const {code, reason} = await chatWSS.closed;
Fermer une connexion WebSocketStream
Un WebSocketStream peut être fermé à l'aide d'une
AbortController
Par conséquent, transmettez un AbortSignal
au constructeur WebSocketStream
.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
Vous pouvez également utiliser la méthode WebSocketStream.close()
,
mais son objectif principal est de permettre
code
et la raison
qui sont envoyées au serveur.
wss.close({code: 4000, reason: 'Game over'});
Amélioration progressive et interopérabilité
Chrome est actuellement le seul navigateur à implémenter l'API WebSocketStream.
Pour assurer l'interopérabilité avec l'API WebSocket classique,
il n'est pas possible d'appliquer une contre-pression aux messages reçus.
Il est possible d'appliquer une contre-pression aux messages envoyés, mais vous devez interroger
WebSocket.bufferedAmount
ce qui est inefficace et non ergonomique.
Détection de caractéristiques
Pour vérifier si l'API WebSocketStream est compatible, utilisez:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
Démo
Sur les navigateurs compatibles, vous pouvez voir l'API WebSocketStream en action dans l'iFrame intégré, ou directement sur Glitch.
Commentaires
L'équipe Chrome souhaite connaître votre avis sur l'API WebSocketStream.
Présentez-nous la conception de l'API
Y a-t-il un aspect de l'API qui ne fonctionne pas comme prévu ? Ou s'il manque des méthodes ou des propriétés dont vous avez besoin pour mettre en œuvre votre idée ? Vous avez une question ou un commentaire sur le modèle de sécurité ? Signalez un problème de spécification dans le dépôt GitHub correspondant : ou ajouter vos réflexions à un problème existant.
Signaler un problème d'implémentation
Avez-vous détecté un bug dans l'implémentation de Chrome ?
Ou l'implémentation est-elle différente des spécifications ?
Signalez un bug sur new.crbug.com.
Veillez à inclure autant de détails que possible, des instructions simples pour reproduire le problème,
et saisissez Blink>Network>WebSockets
dans la zone Composants.
Glitch est idéal pour partager des cas de reproduction rapide et facile.
Apportez votre soutien à l'API
Prévoyez-vous d'utiliser l'API WebSocketStream ? Votre assistance publique permet à l'équipe Chrome de hiérarchiser les fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel de les prendre en charge.
Envoyez un tweet à @ChromiumDev en utilisant le hashtag.
#WebSocketStream
et n'hésitez pas à nous dire où et comment vous l'utilisez.
Liens utiles
- Explication publique
- Démonstration de l'API WebSocketStream | Source de la démonstration de l'API WebSocketStream
- Bug de suivi
- Entrée ChromeStatus.com
- Composant Blink:
Blink>Network>WebSockets
Remerciements
L'API WebSocketStream a été implémentée par Adam Rice et Yutaka Hirano Image héros de Daan Mooij sur Unsplash.