11 pontos por GN⁺ 2025-04-13 | 3 comentários | Compartilhar no WhatsApp
  • 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

 
[Este comentário foi ocultado.]
 
[Este comentário foi ocultado.]
 
GN⁺ 2025-04-13
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 fetch provavelmente vai falhar a requisição com erro

    • Muitas vezes não há necessidade de WebSockets. Server-Sent Events (SSE) é uma solução mais simples. É uma pena que SSE não receba mais atenção
  • Enviar 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 => ...)

    • Requisições de upgrade são confusas. É irritante que o servidor WebSocket fique embutido dentro do servidor HTTP sem estar integrado
    • Em vez de reutilizar middleware que lê headers['authorization'] na requisição WebSocket, é preciso acessar um objeto connectionParams que finge ser cabeçalhos de requisição
    • A API de WebSocket no navegador é mais agradável de usar do que EventSource
  • Streaming 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 arquiteto insiste que precisamos de WebSocket
    • O POC não precisa de XHR nem de WebSocket. É um fluxo de compra sequencial
    • No fim, acabamos entregando WebSockets desnecessários
  • 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 é um protocolo mais simples, projetado para comunicação bidirecional. Com uma única conexão, fica mais fácil controlar o fluxo de dados. É mais fácil gerenciar estado e se recuperar de perda de conexão. Autenticação e controle de acesso ficam mais simples
  • 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