Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Latest commit

 

History

History
225 lines (160 loc) · 13.7 KB

File metadata and controls

225 lines (160 loc) · 13.7 KB
layout title subhead authors date updated hero alt description tags origin_trial url feedback
layouts/blog-post.njk
WebSocketStream: integração de streams com a API WebSocket
Evite que seu aplicativo seja afogado em mensagens WebSocket ou inunde um servidor WebSocket com mensagens aplicando contrapressão.
thomassteiner
2020-03-27
2021-02-23
image/admin/8SrIq5at2bH6i98stCgs.jpg
Uma mangueira de incêndio com água pingando.
WebSocketStream integra streams com a API WebSocket. Isto permite que seu aplicativo aplique contrapressão às mensagens recebidas.
capabilities
api

Histórico

A API WebSocket

A API WebSocket fornece uma interface JavaScript para o protocolo WebSocket, que torna possível abrir uma sessão de comunicação interativa bidirecional entre o navegador do usuário e um servidor. Com essa API, você pode enviar mensagens a um servidor e receber respostas orientadas a eventos sem consultar o servidor para obter uma resposta.

A API Streams

A API Streams permite que o JavaScript acesse programaticamente streams de blocos de dados recebidos pela rede e os processe conforme desejado. Um conceito importante no contexto de streams é a contrapressão. Este é o processo pelo qual um único stream ou pipeline regula a velocidade de leitura ou escrita. Quando o próprio stream ou um stream posterior no pipeline ainda estiver ocupado e ainda não estiver pronto para aceitar mais blocos, ele envia um sinal de volta através do pipeline para retardar a entrega conforme apropriado.

O problema com a API WebSocket atual

Aplicar contrapressão às mensagens recebidas é impossível

Com a API WebSocket atual, a reação a uma mensagem acontece em WebSocket.onmessage, um EventHandler chamado quando uma mensagem é recebida do servidor.

Vamos supor que você tenha um aplicativo que precisa realizar operações pesadas de processamento de dados sempre que uma nova mensagem é recebida. Você provavelmente configuraria o stream de forma semelhante ao código abaixo e, já que chama await para esperar o resultado da process(), certo?

// 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);
};

Errado! O problema com a API WebSocket atual é que não há como aplicar contrapressão. Quando as mensagens chegam mais rápido do que o método process() é capaz de manipulá-las, o processo de renderização ou vai encher a memória armazenando em buffer essas mensagens ou irá parar de responder devido ao uso de 100% da CPU, ou as duas coisas.

Aplicar contrapressão às mensagens enviadas não é ergonômico

Aplicar contrapressão às mensagens enviadas é possível, mas envolve a monitoração da propriedade WebSocket.bufferedAmount, que é ineficiente e não ergonômica. Esta propriedade somente-leitura retorna o número de bytes de dados que foram enfileirados usando chamadas para WebSocket.send(), mas ainda não foram transmitidos para a rede. Este valor é zerado quando todos os dados enfileirados são enviados, mas se você continuar chamando WebSocket.send(), ele continuará a subir.

O que é a API WebSocketStream? {: #what }

A API WebSocketStream lida com o problema de contrapressão inexistente ou não ergonômica integrando streams com a API WebSocket. Isto significa que a contrapressão pode ser aplicada "gratuitamente", sem nenhum custo extra.

Casos de uso sugeridos para a API WebSocketStream {: # use-cases}

Exemplos de sites que podem usar essa API incluem:

  • Aplicativos WebSocket de alta largura de banda que precisam manter a interatividade, em particular vídeo e compartilhamento de tela.
  • Da mesma forma, capturas de vídeo e outros aplicativos que geram muitos dados no navegador que precisam ser transferidos para o servidor. Com a contrapressão, o cliente pode interromper a produção de dados em vez de ficar acumulando dados na memória.

Status atual {: #status}

Passo Status
1. Criar um explicador Concluído
2. Criar o rascunho inicial das especificações Em andamento
3. Obter feedback e repetir o design Em andamento
4. Prova de origem Concluída
5. Lançamento Não iniciado

Como usar a API WebSocketStream {: #use}

Exemplo introdutório

A API WebSocketStream é baseada em promessas, o que faz com que a forma de usá-la seja natural num mundo JavaScript moderno. Você começa construindo um novo WebSocketStream e passando a ele a URL do servidor WebSocket. Em seguida, você espera que a connection seja estabelecida, o que resulta num ReadableStream e/ou um WritableStream.

Ao chamar o método ReadableStream.getReader(), você finalmente obtém um ReadableStreamDefaultReader, do qual pode chamar read() para ler dados até que o stream seja concluído, ou seja, até que ele retorne um objeto da forma {value: undefined, done: true}.

Da mesma forma, ao chamar o método WritableStream.getWriter(), você finalmente obtém um WritableStreamDefaultWriter, onde você pode chamar write() para gravar dados.

  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);
  }

Contrapressão

E o prometido recurso de contrapressão? Como escrevi acima, você o obtém "de graça", sem a necessidade de passos adicionais. Se process() demorar mais, a mensagem seguinte só será consumida quando o pipeline estiver pronto. Da mesma forma, o passo WritableStreamDefaultWriter.write() só será realizado se for seguro fazê-lo.

Exemplos avançados

O segundo argumento para WebSocketStream é um pacote de opções para permitir extensões futuras. Atualmente, a única opção é protocols, que se comporta da mesma forma que o segundo argumento passado para o construtor WebSocket:

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

O protocol selecionado, bem como as extensions potenciais, fazem parte do dicionário disponível por meio da promessa WebSocketStream.connection. Todas as informações sobre a conexão ao vivo são fornecidas por esta promessa, uma vez que elas não são relevantes se a conexão falhar.

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

Informações sobre a conexão WebSocketStream fechada

A informação que estava disponível a partir dos eventos WebSocket.onclose e WebSocket.onerror na API WebSocket está agora disponível através da promessa WebSocketStream.closed. A promessa rejeita no caso de um fechamento impuro, caso contrário, resolve para o código e motivo enviado pelo servidor.

Todos os códigos de status possíveis e seus significados são explicados na lista de códigos de status CloseEvent.

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

Fechando uma conexão WebSocketStream

Um WebSocketStream pode ser fechado com um AbortController. Portanto, passe um AbortSignal para o construtor WebSocketStream.

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

Como alternativa, você também pode usar o método WebSocketStream.close(), mas seu objetivo principal é permitir especificar o código e o motivo que é enviado ao servidor.

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

Aprimoramento progressivo e interoperabilidade

O Chrome é atualmente o único navegador a implementar a API WebSocketStream. Para interoperabilidade com a API WebSocket clássica, não é possível aplicar contrapressão às mensagens recebidas. Aplicar contrapressão às mensagens enviadas é possível, mas envolve monitorar a propriedade WebSocket.bufferedAmount, que é ineficiente e não ergonômica.

Detecção de recursos

Para verificar se a API WebSocketStream é suportada, use:

if ('WebSocketStream' in window) {
  // `WebSocketStream` is supported!
}

Demonstração

Em navegadores compatíveis, você pode ver a API WebSocketStream em ação no iframe incorporado ou diretamente no Glitch.

{% Glitch { id: 'websocketstream-demo', path: 'public/index.html' } %}

Feedback {: #feedback }

A equipe do Chrome quer saber mais sobre suas experiências com a API WebSocketStream.

Conte-nos sobre o design da API

Existe algo na API que não funciona conforme o esperado? Ou faltam propriedades de que você precisa para implementar sua ideia? Registre um issue de especificação no repositório do GitHub correspondente ou acrescente suas ideias a um issue existente.

Relate um problema com a implementação

Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente da especificação? Informe um bug em new.crbug.com. Certifique-se de incluir o máximo de detalhes possível, fornecer instruções simples para reproduzir o bug e configurar os Componentes Blink>PerformanceAPIs. Glitch funciona muito bem para compartilhar reproduções rápidas e fáceis.

Mostre seu apoio à API

Você está planejando usar a API WebSocketStream? Seu suporte público ajuda a equipe do Chrome a priorizar os recursos e mostra a outros fornecedores de navegadores como o apoio é fundamental.

Envie um tweet para @ChromiumDev usando a hashtag #WebSocketStream e diga-nos onde e como você está usando a API.

Links úteis {: #helpful }

Agradecimentos

A API WebSocketStream foi implementada por Adam Rice e Yutaka Hirano. Imagem herói por Daan Mooij no Unsplash.