Saltar al contenido principal




Evite que su aplicación se ahogue en mensajes de WebSocket o inunde un servidor de WebSocket con mensajes aplicando contrapresión.

Antecedentes

La API de WebSocket

los API de WebSocket
proporciona una interfaz JavaScript para Protocolo WebSocket, lo que permite abrir una sesión de comunicación interactiva bidireccional entre el navegador del usuario y un servidor. Con esta API, puede enviar mensajes a un servidor y recibir respuestas basadas en eventos sin sondear el servidor para obtener una respuesta.

La API de Streams

los API de Streams
permite que JavaScript acceda mediante programación a flujos de datos recibidos a través de la red y los procese como desee. Un concepto importante en el contexto de las corrientes es
contrapresión. Este es el proceso mediante el cual una sola corriente o una 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 tuberías todavía está ocupada y aún no está lista 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

Aplicar contrapresión a los mensajes recibidos es imposible

Con la API de WebSocket actual, la reacción a un mensaje ocurre en
WebSocket.onmessage, un EventHandler se llama cuando se recibe un mensaje del servidor.

Supongamos que tiene una aplicación que necesita realizar grandes operaciones de procesamiento de datos cada vez que se recibe un mensaje nuevo. Probablemente configuraría el flujo similar al código a continuación, y dado que await el resultado de la process() llama, deberías estar bien, ¿verdad?


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 process(data);
};

¡Incorrecto! El problema con la API de WebSocket actual es que no hay forma de aplicar contrapresión. Cuando los mensajes llegan más rápido que el process() El método puede manejarlos, el proceso de renderizado llenará la memoria almacenando esos mensajes en búfer, dejará de responder debido al uso del CPU al 100%, o ambos.

Aplicar contrapresión a los mensajes enviados no es ergonómico

Es posible aplicar contrapresión a los mensajes enviados, pero implica sondear el
WebSocket.bufferedAmount

propiedad, que es ineficiente y no ergonómica. Esta propiedad de solo lectura devuelve el número de bytes de datos que se han puesto en cola mediante llamadas a
WebSocket.send(), pero aún no transmitido a la red. Este valor se restablece a cero una vez que se han enviado todos los datos en cola, pero si sigue llamando WebSocket.send(), seguirá subiendo.

¿Qué es la API WebSocketStream?

La API de WebSocketStream se ocupa del problema de la contrapresión inexistente o no ergonómica mediante la integración de flujos con la API de WebSocket. Esto significa que la contrapresión se puede aplicar «gratis», sin ningún costo adicional.

Casos de uso sugeridos para la API WebSocketStream

Ejemplos de sitios que pueden usar esta API incluyen:

  • Aplicaciones WebSocket de gran ancho de banda que necesitan mantener la interactividad, en particular, compartir video y pantalla.
  • Del mismo modo, la captura de video y otras aplicaciones que generan una gran cantidad de datos en el navegador que deben cargarse en el servidor. Con contrapresión, el cliente puede dejar de producir datos en lugar de acumular datos en la memoria.

Estado actual

Paso Estado
1. Crea un explicador Completar
2. Crear borrador inicial de especificación En progreso
3. Recopile comentarios y repita el diseño En progreso
4. Prueba de origen Completar
5. Lanzamiento No empezado

Cómo utilizar 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 JavaScript moderno. Empiece por construir un nuevo WebSocketStream y pasándole la URL del servidor WebSocket. A continuación, espere el connection que se establezca, lo que da lugar a una
ReadableStream

y / o un
WritableStream.

Llamando al
ReadableStream.getReader()

método, finalmente obtienes un
ReadableStreamDefaultReader, que luego puedes read()

datos desde hasta que finaliza la secuencia, es decir, hasta que devuelve un objeto del formulario
{value: undefined, done: true}.

En consecuencia, llamando al
WritableStream.getWriter()

método, finalmente obtienes un
WritableStreamDefaultWriter, que luego puedes write()

datos a.

  const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.connection;
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 escribí anteriormente, lo obtienes «gratis», no se necesitan pasos adicionales. Si process() toma más tiempo, el siguiente mensaje solo se consumirá una vez que la canalización esté lista. Asimismo el WritableStreamDefaultWriter.write() El paso solo continuará si es seguro hacerlo.

Ejemplos avanzados

El segundo argumento de WebSocketStream es una bolsa de opciones para permitir una extensión futura. Actualmente la única opción es protocols, que se comporta igual que el
segundo argumento para el constructor de WebSocket:

const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.connection;

El seleccionado protocol así como potencial extensions son parte del diccionario disponible a través del WebSocketStream.connection promesa. Toda la información sobre la conexión en vivo es proporcionada por esta promesa, ya que no es relevante si falla la conexión.

const {readable, writable, protocol, extensions} = await chatWSS.connection;

Información sobre la conexión WebSocketStream cerrada

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 WebSocketStream.closed promesa. La promesa se rechaza en caso de un cierre no limpio, de lo contrario se resuelve con el código y el motivo enviado por el servidor.

Todos los códigos de estado posibles y su significado se explican en la
lista de CloseEvent códigos de estado.

const {code, reason} = await chatWSS.closed;

Cerrar una conexión WebSocketStream

Un WebSocketStream se puede cerrar con un
AbortController. Por lo tanto, pase un AbortSignal

al WebSocketStream constructor.

const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);

Como alternativa, también puede utilizar el WebSocketStream.close() método, pero su propósito principal es permitir especificar el
código
y motivo por el que se envía al servidor.

wss.close({code: 4000, reason: 'Game over'});

Interoperabilidad y mejora progresiva

Actualmente, Chrome es el único navegador que implementa la API WebSocketStream. Para la interoperabilidad con la API de WebSocket clásica, no es posible aplicar contrapresión a los mensajes recibidos. Es posible aplicar contrapresión a los mensajes enviados, pero implica sondear el
WebSocket.bufferedAmount

propiedad, que es ineficiente y no ergonómica.

Detección de características

Para comprobar si la API de WebSocketStream es compatible, utilice:

if ('WebSocketStream' in window) {
}

Manifestación

En los navegadores compatibles, puede ver la API de WebSocketStream en acción en el iframe incrustado, o directamente en Glitch.

Realimentación

El equipo de Chrome desea conocer sus experiencias con la API WebSocketStream.

Cuéntanos sobre el diseño de la API

¿Hay algo en la API que no funciona como esperabas? ¿O faltan métodos o propiedades que necesita para implementar su idea? ¿Tiene alguna pregunta o comentario sobre el modelo de seguridad? Presentar un problema de especificaciones en el correspondiente Repositorio de GitHubo agregue sus pensamientos a un problema existente.

Informar un problema con la implementación

¿Encontraste un error con la implementación de Chrome? ¿O la implementación es diferente de la especificación? Presentar un error en new.crbug.com. Asegúrese de incluir todos los detalles que pueda, instrucciones simples para reproducir e ingrese Blink>Network>WebSockets en el Componentes caja.
Falla funciona muy bien para compartir casos de reproducción rápida y fácil.

Mostrar apoyo a la API

¿Está pensando en utilizar la API de WebSocketStream? Su soporte público ayuda al equipo de Chrome a priorizar funciones y muestra a otros proveedores de navegadores lo importante que es brindarles soporte.

Enviar un tweet a @Cromodev con el #WebSocketStream hashtag y háganos saber dónde y cómo lo está usando.

Enlaces Útiles

Agradecimientos

La API WebSocketStream fue implementada por Adam Rice y
Yutaka Hirano. Imagen de héroe de Daan Mooij en
Unsplash.

R Marketing Digital