Aplica la contrapresión para evitar que tu app se ahogue en los mensajes de WebSocket o que sature un servidor de WebSocket de mensajes.
Información general
La API de WebSocket
La API de WebSocket Proporciona una interfaz de JavaScript para el protocolo de WebSocket. lo que permite iniciar una sesión de comunicación bidireccional interactiva. entre el navegador del usuario y un servidor. Con esta API, puedes enviar mensajes a un servidor y recibir respuestas controladas por eventos sin sondear el servidor para obtener una respuesta.
La API de Streams
La API de Streams Permite que JavaScript acceda de manera programática a flujos de fragmentos de datos recibidos a través de la red y procesarlos como desees. Un concepto importante en el contexto de las transmisiones es contrapresión. Este es el proceso mediante el cual una sola transmisión o cadena de tuberías regula la velocidad de lectura o escritura. Cuando la transmisión en sí o una transmisión posterior en la cadena de canalización sigue ocupada. y aún no está listo para aceptar más fragmentos, envía una señal hacia atrás a través de la cadena para ralentizar la entrega según corresponda.
El problema con la API de WebSocket actual
Es imposible aplicar la contrapresión a los mensajes recibidos.
Con la API de WebSocket actual, la reacción a un mensaje se produce
WebSocket.onmessage
:
Se llama a EventHandler
cuando se recibe un mensaje del servidor.
Supongamos que tienes una aplicación que necesita realizar operaciones pesadas de procesamiento de datos
cada vez que se recibe un mensaje nuevo.
Es probable que configurarías el flujo de manera similar al siguiente código:
y, como await
el resultado de la llamada a process()
, deberías estar bien, ¿no?
// 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);
};
Incorrecto. El problema con la API de WebSocket actual es que no hay forma de aplicar la contrapresión.
Cuando los mensajes llegan más rápido de lo que el método process()
puede manejarlos,
el proceso de renderización llenará
la memoria almacenando en búfer esos mensajes
no responden debido al 100% del uso de la CPU, o a ambos.
La contrapresión en los mensajes enviados no es ergonómico
Es posible aplicar la contrapresión a los mensajes enviados, pero implica sondear
WebSocket.bufferedAmount
que es ineficiente y no ergonómico.
Esta propiedad de solo lectura muestra la cantidad de bytes de datos que se colocaron en cola
usar llamadas para
WebSocket.send()
:
pero aún no se transmiten a la red.
Este valor se restablece a cero
luego de que se envían todos los datos en cola
pero si sigues llamando a WebSocket.send()
,
seguirá subiendo.
¿Qué es la API de WebSocketStream?
La API de WebSocketStream se encarga del problema de la contrapresión inexistente o no ergonómica. a través de la integración de transmisiones con la API de WebSocket. Esto significa que la contrapresión se puede aplicar "de forma gratuita" sin ningún costo adicional.
Casos de uso sugeridos para la API de WebSocketStream
Estos son algunos ejemplos de sitios que pueden usar esta API:
- Aplicaciones de WebSocket de alto ancho de banda que necesitan conservar la interactividad en particular el video y la pantalla compartida.
- Del mismo modo, la captura de video y otras aplicaciones que generan una gran cantidad de datos en el navegador que debe subirse al servidor. Con la contrapresión, el cliente puede dejar de producir datos en lugar de acumularlos en la memoria.
Estado actual
Paso | Estado |
---|---|
1. Crear explicación | Completo |
2. Crea el borrador inicial de la especificación | En curso |
3. Recopila comentarios y iterar en el diseño | En curso |
4. Prueba de origen | Completo |
5. Lanzamiento | Sin iniciar |
Cómo usar la API de WebSocketStream
Ejemplo introductorio
La API de WebSocketStream se basa en promesas, lo que hace que lidiar con ella se sienta natural.
en un mundo moderno de JavaScript.
Para comenzar, debes construir un WebSocketStream
nuevo y pasarle la URL del servidor de WebSocket.
Luego, espera a que la conexión sea opened
,
lo que da como resultado un
ReadableStream
y/o
WritableStream
Al llamar al
ReadableStream.getReader()
Por último, obtienes un
ReadableStreamDefaultReader
,
que luego podrás read()
desde el momento en que se completa la transmisión, es decir, hasta que se muestra un objeto con el formato
{value: undefined, done: true}
Por lo tanto, al llamar al
WritableStream.getWriter()
Por último, obtienes un
WritableStreamDefaultWriter
,
que luego podrás write()
a la que se envían los datos.
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);
}
Contrapresión
¿Qué pasa con la función de contrapresión prometida?
Como mencioné antes, lo obtienes "gratis", sin necesidad de realizar pasos adicionales.
Si process()
lleva más tiempo, el siguiente mensaje solo se consumirá una vez que la canalización esté lista.
Del mismo modo, el paso WritableStreamDefaultWriter.write()
solo procederá si es seguro hacerlo.
Ejemplos avanzados
El segundo argumento de WebSocketStream es una bolsa de opciones para permitir extensiones futuras.
Actualmente, la única opción es protocols
,
que se comporta igual que
segundo argumento para el constructor de WebSocket:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
El protocol
seleccionado y los posibles extensions
forman parte del diccionario
disponible a través de la promesa WebSocketStream.opened
.
Esta promesa proporciona
toda la información sobre la conexión en vivo
ya que no es relevante si la conexión falla.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
Información sobre la conexión cerrada de WebSocketStream
La información que estaba disponible en el
WebSocket.onclose
y
WebSocket.onerror
eventos
en la API de WebSocket ahora está disponible a través de la promesa WebSocketStream.closed
.
La promesa se rechaza en caso de un cierre sucio
de lo contrario, se resuelve en el código y el motivo que envió el servidor.
Todos los códigos de estado posibles y su significado se explican en el
lista de CloseEvent
códigos de estado.
const {code, reason} = await chatWSS.closed;
Cómo cerrar una conexión de WebSocketStream
Un WebSocketStream se puede cerrar con un
AbortController
Por lo tanto, pasa un objeto AbortSignal
.
al constructor WebSocketStream
.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
Como alternativa, también puedes usar el método WebSocketStream.close()
,
pero su propósito principal es permitir que se especifiquen los
código
y el motivo que se envía al servidor.
wss.close({code: 4000, reason: 'Game over'});
Interoperabilidad y mejora progresiva
En este momento, Chrome es el único navegador que implementa la API de WebSocketStream.
Para la interoperabilidad con la API clásica de WebSocket,
no es posible aplicar la contrapresión a los mensajes recibidos.
Es posible aplicar la contrapresión a los mensajes enviados, pero implica sondear
WebSocket.bufferedAmount
que es ineficiente y no ergonómico.
Detección de funciones
Para verificar si la API de WebSocketStream es compatible, usa lo siguiente:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
Demostración
En los navegadores compatibles, puedes ver la API de WebSocketStream en acción en el iframe incorporado, o directamente en Glitch.
Comentarios
El equipo de Chrome quiere conocer tus experiencias con la API de WebSocketStream.
Cuéntanos sobre el diseño de la API
¿Algo en la API no funciona como esperabas? ¿O faltan métodos o propiedades que necesites para implementar tu idea? ¿Tienes alguna pregunta o comentario sobre el modelo de seguridad? Informa un problema de especificaciones en el repositorio de GitHub correspondiente. o agrega lo que piensas a un problema existente.
Informar un problema con la implementación
¿Encontraste un error en la implementación de Chrome?
¿O la implementación es diferente de la especificación?
Informa un error en new.crbug.com.
Asegúrate de incluir tantos detalles como puedas, instrucciones sencillas para la reproducción,
y, luego, ingresa Blink>Network>WebSockets
en el cuadro Componentes.
Glitch funciona muy bien para compartir casos de reproducción de forma rápida y sencilla.
Demuestra compatibilidad con la API
¿Planeas usar la API de WebSocketStream? Tu asistencia pública ayuda al equipo de Chrome a priorizar funciones y muestra a otros proveedores de navegadores la importancia de brindar asistencia.
Envía un tweet a @ChromiumDev con el hashtag
#WebSocketStream
y cuéntanos dónde y cómo la utilizas.
Vínculos útiles
- Explicación pública
- Demostración de la API de WebSocketStream | Fuente de la demostración de la API de WebSocketStream
- Seguimiento de errores
- Entrada de ChromeStatus.com
- Componente de parpadeo:
Blink>Network>WebSockets
Agradecimientos
Adam Rice implementó la API de WebSocketStream y Yutaka Hirano. Hero image de Daan Mooij en Retiro: