- WebSocket é útil para comunicação em tempo real, mas nem sempre é necessário, e alternativas baseadas em HTTP podem ser mais simples e estáveis
- Em processamento transacional, gerenciamento de conexões e complexidade do servidor, WebSocket pode causar overhead excessivo
- Com HTTP Streaming e a biblioteca
eventkit, é possível fazer sincronização em tempo real e processamento de eventos sem WebSocket
O que é WebSocket
- WebSocket é uma tecnologia que abre um canal persistente de comunicação bidirecional entre cliente e servidor
- A conexão começa via HTTP, mas depois a comunicação passa a ocorrer por um protocolo separado
- É usado com frequência para implementar aplicações em tempo real e é útil por permitir comunicação nos dois sentidos
Mensagens WebSocket não seguem um modelo transacional
- WebSocket não garante uma associação direta entre requisição e resposta
- Comandos de mudança de estado e mensagens de resultado podem chegar misturados no mesmo fluxo
- Por exemplo, mesmo que um cliente altere o estado e ocorra um erro, pode ser difícil saber a qual comando aquele erro se refere
- Uma forma de resolver isso é incluir
requestId para ligar comando e resposta, mas isso aumenta a complexidade e o custo de manutenção
- Enviar comandos por meio de um modelo transacional com HTTP e usar WebSocket apenas para transmitir mudanças de estado é mais simples
- É possível separar o lado de envio com requisições HTTP e o lado de recebimento com WebSocket ou outro método de streaming
A dificuldade de gerenciar o ciclo de vida da conexão WebSocket
- Ao usar WebSocket, é preciso tratar manualmente início da conexão, encerramento, erros, reconexão e outros casos
- Um exemplo básico de tratamento no navegador inclui lidar com eventos de abertura da conexão, recebimento de mensagens, erro e fechamento
- Também são necessárias lógicas adicionais, como reconexão, buffering de mensagens e backoff exponencial
- Em contrapartida, no HTTP o início e o fim são claros em cada requisição, o que simplifica a implementação
- Esse gerenciamento complexo do ciclo de vida só se justifica quando houver um motivo claro para usar WebSocket
Aumento da complexidade do código do servidor
- WebSocket exige tratar requisições de upgrade do HTTP, o que pede lógica adicional de handshake
- É necessário validar headers especiais como
Sec-WebSocket-Key e retornar corretamente os headers de resposta
- Depois que a conexão WebSocket é estabelecida, também é preciso manter o estado contínuo de recebimento e envio de mensagens, e podem surgir problemas como tratamento de frames parciais
- Em comparação com usar apenas HTTP, depuração e tratamento de erros ficam mais difíceis
- Frameworks abstraem parte desse processo, mas a complexidade fundamental continua existindo
Alternativa: HTTP Streaming
- HTTP já é, por natureza, um protocolo que suporta streaming, permitindo transmitir um fluxo de dados em tempo real em vez de um arquivo completo
- É possível substituir apenas o lado de recebimento do WebSocket por HTTP streaming
- Com geradores assíncronos, dá para processar atualizações de estado em formato de stream
- Fluxo no servidor
- Atualizações de estado são feitas na função que processa os comandos
- Clientes conectados recebem novos valores por meio do gerador sempre que eles surgem
- Comandos de alteração de estado são enviados via HTTP POST, e o stream em tempo real é assinado com uma requisição GET
- Fluxo no cliente
- Recebimento de dados em tempo real via Fetch API e Stream Reader
- Atualização da UI após a decodificação do texto
- Com essa estrutura, é possível implementar sincronização de estado em tempo real sem WebSocket
Bônus: apresentação da biblioteca eventkit
eventkit é uma biblioteca que facilita compor e observar streams assíncronos
- É semelhante ao RxJS, mas com melhor gerenciamento de efeitos colaterais e design baseado em geradores
- Ao enviar atualizações de estado para um stream, o cliente pode recebê-las em tempo real
- Com
Stream e AsyncObservable, é possível implementar isso de forma simples tanto no servidor quanto no cliente
- Uso de eventkit no servidor
- Envia mudanças de estado com push para um Stream, e os clientes assinam esse stream
- Uso de eventkit no cliente
- Recebe os dados do stream, decodifica e atualiza a UI
- O repositório oficial no GitHub e um guia de HTTP Streaming também estão disponíveis
GitHub: https://github.com/hntrl/eventkit
3 comentários
Comentários do Hacker News
Acho que o streaming HTTP não foi projetado com esse padrão em mente. Streaming HTTP serve para dividir dados grandes em partes. Se você usar streaming como um mecanismo pub/sub, pode se arrepender. Intermediários HTTP não esperam esse padrão de tráfego (NGINX, CloudFlare etc.). Sempre que a conexão Wi‑Fi cair, a API
fetchprovavelmente vai falhar a requisição com erroEnviar um RequestID ao servidor para obter um ciclo de requisição/resposta não é estranho nem exagerado. Em apps sérios, sempre vale a pena ter uma API como
send(message).then(res => ...)headers['authorization']na requisição WebSocket, é preciso acessar um objetoconnectionParamsque finge ser cabeçalhos de requisiçãoStreaming de vídeo funciona com o cliente pedindo chunks por intervalo, não por uma única conexão HTTP
É melhor usar SSE em vez de EventKit
No POC, vou usar envio tradicional de formulário HTTP. Não preciso de mais nada
O problema do HTTP2 é que o server push foi adicionado em cima de um protocolo já existente. HTTP é um protocolo de transporte de recursos, o que adiciona overhead desnecessário. O objetivo principal do HTTP2 é permitir que o servidor envie arquivos/recursos antecipadamente ao cliente para reduzir a latência de ida e volta
WebSockets não enviam como stream, e sim como datagramas (pacotes). A API de WebSockets das bibliotecas JavaScript não consegue lidar com backpressure e não consegue tratar todos os erros. É preciso cuidado ao usar isso como um stream TCP
Me arrependi depois de colocar WebSockets em produção. Houve problemas como o NGINX encerrar conexões após 4/8 horas e o navegador não se reconectar depois de sair do modo de suspensão. Sempre que possível, deve-se evitar WebSockets e conexões de longa duração
Existe uma visão idealizada sobre WebSockets. Há uma tendência de usá-los para casos de streaming/tempo real. WebSockets fazem você perder a simplicidade e as vantagens das ferramentas HTTP. A solução para mudanças em servidores de streaming é h2/h3 com SSE. Se for possível agrupar em lote até no máximo 0,5 req/s por cliente, não há necessidade de WebSockets
Quem tem interesse em streaming HTTP deveria conferir o Braid-HTTP. Ele estende o HTTP de forma elegante para streaming de eventos e oferece um protocolo poderoso de sincronização de estado