Conexões persistentes e tempo real: o que muda na infraestrutura

CONTEÚDO TBF HOST

Conexões persistentes e tempo real: o que muda na infraestrutura

Recursos de tempo real ficaram comuns: notificações que aparecem sozinhas, chat de atendimento, painéis que atualizam sem recarregar, indicadores de progresso durante uma operação longa.

Do lado do produto, são recursos simples de descrever. Do lado da infraestrutura, eles mudam uma premissa fundamental: em vez de conexões curtas que abrem e fecham, a aplicação passa a manter milhares de conexões abertas ao mesmo tempo.

Isso altera o que dimensionar, o que configurar no proxy e como escalar. Este guia trata dessas consequências. A arquitetura de produção está no artigo sobre aplicações Node.js em produção, e o dimensionamento por carga de requisições, no artigo sobre como dimensionar um servidor para Node.js.

A diferença fundamental

Em uma aplicação web tradicional, o navegador pede, o servidor responde e a conexão termina. Um visitante lendo uma página por dez minutos não consome nada do servidor durante esse tempo — ele já recebeu tudo.

Em uma aplicação com tempo real, a conexão permanece aberta enquanto a pessoa está com a página aberta. É o que permite ao servidor enviar informação sem que o navegador peça.

A consequência é uma mudança de unidade de medida:

TradicionalTempo real
Unidade de cargaRequisições por segundoConexões simultâneas
DuraçãoMilissegundosMinutos ou horas
Usuário ocioso consomeNadaUma conexão
Limite típicoProcessamentoMemória e descritores
Efeito de uma reinicializaçãoQuase nenhumTodos desconectam

A terceira linha é a que mais surpreende: uma pessoa com a aba aberta e sem fazer nada continua ocupando recursos. Em um painel que fica aberto o dia inteiro em uma equipe de trinta pessoas, são trinta conexões permanentes, mesmo que ninguém esteja olhando.

Por que o Node.js aparece muito aqui

Vale explicar, porque não é coincidência.

Manter muitas conexões abertas é justamente o cenário em que o modelo de execução do Node.js se destaca: conexões ociosas quase não custam processamento, e um único processo consegue manter um número grande delas.

Em modelos que dedicam um processo ou uma linha de execução por conexão, o custo cresce rapidamente — cada conexão ocupa memória mesmo sem tráfego.

Isso não significa que outras tecnologias não sirvam. Significa que a arquitetura de tempo real é um caso em que a escolha do Node.js tem uma justificativa técnica concreta, e não apenas de preferência da equipe.

O que dimensionar

As três restrições que aparecem, em ordem de frequência:

1. Descritores de arquivo

O limite menos conhecido e o primeiro a ser atingido.

O sistema operacional impõe um teto ao número de conexões simultâneas que um processo pode manter. Esse limite costuma ser modesto por padrão — pensado para aplicações comuns — e precisa ser ajustado para aplicações de tempo real.

O sintoma é característico: a aplicação aceita conexões normalmente até um número específico e então passa a recusar todas as novas, com o servidor tranquilo. Como o número é sempre o mesmo, o padrão é reconhecível.

Ajustar isso exige controle sobre o sistema operacional, o que é um dos requisitos concretos que excluem ambientes compartilhados.

2. Memória por conexão

Cada conexão aberta consome memória — pouca, individualmente, mas multiplicada por milhares torna-se a restrição principal.

O consumo depende do que a aplicação guarda por conexão: identificação do usuário, estado da sessão, mensagens em espera. Aplicações que acumulam dados por conexão consomem muito mais, e esse é o principal fator de dimensionamento.

A medição necessária: conectar um número conhecido de clientes e observar o crescimento da memória. Isso dá o custo por conexão, que é o número que permite calcular a capacidade.

3. Processamento na hora do envio

Conexões ociosas custam pouco. O custo aparece quando há mensagens a distribuir.

Enviar uma atualização para dez mil conexões simultâneas é trabalho real, e a forma como a aplicação faz isso importa: distribuir individualmente é muito mais caro que agrupar destinatários. Em aplicações com muitas atualizações por segundo, esse é o gargalo.

O proxy reverso precisa de atenção

Um ponto de configuração que quebra aplicações de tempo real e cuja causa não é óbvia.

Proxies reversos são configurados por padrão para conexões curtas. Aplicados sem ajuste a conexões persistentes, eles produzem dois comportamentos indesejados:

  • Encerramento por inatividade. O proxy fecha conexões que ficam um tempo sem tráfego, entendendo que estão paradas. Em tempo real, uma conexão sem tráfego é normal — é exatamente o estado de espera.
  • Recusa de conexões persistentes, quando o proxy não está configurado para permitir a transição do tipo de conexão.

O primeiro produz o sintoma mais relatado: a conexão cai sozinha depois de alguns minutos e reconecta. Funciona, mas com interrupções periódicas que confundem quem está depurando, porque não há erro na aplicação.

Duas medidas resolvem:

  1. Ajustar o tempo limite de inatividade no proxy, com valor compatível com a aplicação.
  2. Manter um sinal periódico entre cliente e servidor, que também serve para detectar conexões mortas.

A segunda medida é boa prática independentemente do proxy: sem ela, conexões que caíram sem aviso — queda de rede, aparelho desligado — permanecem contabilizadas como abertas, inflando a contagem e consumindo recursos por nada.

Escalar é diferente

Aqui está a consequência mais séria, e a menos antecipada.

Quando a aplicação roda em um único processo, todas as conexões estão no mesmo lugar e enviar uma mensagem a todos é trivial. Ao distribuir a carga entre vários processos ou servidores, cada um conhece apenas as próprias conexões.

O problema prático: um usuário conectado ao processo A precisa receber uma mensagem originada no processo B. Sem um mecanismo de comunicação entre eles, a mensagem simplesmente não chega.

A solução padrão é um intermediário que distribui as mensagens entre todos os processos, de modo que cada um entregue às conexões que mantém. Isso adiciona um componente ao ambiente — que precisa ser monitorado e mantido.

Há também uma questão de distribuição de carga: conexões persistentes não se redistribuem sozinhas. Um servidor que entra no conjunto não recebe as conexões já abertas em outros — ele só recebe as novas. Isso significa que a carga demora a equilibrar, e que adicionar capacidade durante um pico tem efeito mais lento do que em aplicações tradicionais.

Publicar derruba todo mundo

Uma consequência operacional que muda a rotina de publicação.

Ao reiniciar a aplicação, todas as conexões caem ao mesmo tempo. Os clientes tentam reconectar — e, se todos tentarem simultaneamente, produzem uma rajada que pode impedir a aplicação de subir.

O que reduz o impacto:

  • Reconexão com espera crescente e variação aleatória no cliente, para que as tentativas se distribuam no tempo em vez de se concentrarem.
  • Publicação gradual, reiniciando processos em sequência em vez de todos de uma vez.
  • Estado no servidor, não na conexão, para que reconectar restaure a situação sem perda.
  • Publicar em janelas de menor uso, quando possível.

O primeiro item é responsabilidade do código do cliente e é a medida mais eficaz. Sem ela, cada reinicialização vira um teste de carga involuntário — e o pior momento possível para recebê-lo é logo após uma publicação.

Nem tudo precisa ser tempo real

Vale a delimitação, porque a complexidade é real e nem sempre justificada.

Alternativas mais simples que atendem muitos casos:

  • Consulta periódica. O navegador pergunta a cada intervalo se há novidade. É simples, funciona em qualquer infraestrutura e atende quando um atraso de alguns segundos é aceitável.
  • Fluxo unidirecional do servidor, quando só o servidor precisa enviar e o cliente não precisa responder pelo mesmo canal — mais simples que uma conexão bidirecional completa.
  • Notificações por outro canal, como e-mail ou mensagem, quando a informação não precisa aparecer na tela naquele instante.

A primeira alternativa tem má reputação e merece defesa: para muitos casos, uma consulta a cada dez segundos é indistinguível de tempo real para o usuário e dispensa toda a complexidade descrita neste artigo. O custo é tráfego adicional, que só importa em escala.

A pergunta que orienta: um atraso de alguns segundos prejudicaria a experiência? Se não, provavelmente não é necessário manter conexões abertas.

O que monitorar

Indicadores específicos para esse tipo de aplicação:

  • Número de conexões abertas, por processo e no total.
  • Taxa de conexão e desconexão, cujo aumento repentino indica instabilidade.
  • Conexões por processo, para verificar o equilíbrio da distribuição.
  • Memória por processo, correlacionada ao número de conexões.
  • Tempo de entrega das mensagens, que revela saturação antes da queda.
  • Proximidade do limite de descritores, para agir antes de as recusas começarem.

O último é o que evita o incidente mais evitável de todos: saber que o limite está próximo permite ajustar antes de a aplicação começar a recusar conexões.

Conclusão

Aplicações de tempo real trocam a unidade de medida: em vez de requisições por segundo, conexões simultâneas. Isso muda o que limita — memória e descritores no lugar de processamento — e torna um usuário ocioso um consumidor de recursos.

Três consequências definem o trabalho de infraestrutura: o proxy reverso precisa ser configurado para conexões longas, sob pena de quedas periódicas sem erro aparente; escalar exige um mecanismo de comunicação entre processos, ou mensagens não chegam a parte dos usuários; e cada publicação derruba todas as conexões de uma vez, o que exige reconexão bem construída do lado do cliente.

E vale a pergunta antes de adotar: um atraso de alguns segundos prejudicaria a experiência? Se não, uma consulta periódica resolve sem nada disso. Se sim, o ambiente precisa permitir processos permanentes e ajuste de limites do sistema — conheça o Cloud Server para Node.js da TBF Host e avalie a configuração adequada.

Perguntas frequentes

O que muda na infraestrutura em uma aplicação de tempo real?

A unidade de carga passa a ser conexões simultâneas em vez de requisições por segundo. As conexões permanecem abertas por minutos ou horas, um usuário ocioso continua consumindo recursos, e os limites deixam de ser de processamento e passam a ser de memória e de descritores do sistema operacional.

Por que minha conexão de tempo real cai sozinha depois de alguns minutos?

Quase sempre é o proxy reverso encerrando conexões por inatividade. Ele é configurado por padrão para conexões curtas e entende que uma conexão sem tráfego está parada — mas em tempo real essa é a situação normal. Ajustar o tempo limite e manter um sinal periódico resolve.

Quantas conexões simultâneas um servidor aguenta?

Depende de dois limites: o número máximo de descritores permitido pelo sistema operacional, que costuma ser modesto por padrão e precisa ser ajustado, e a memória consumida por conexão, que varia conforme o que a aplicação guarda para cada uma. É preciso medir os dois.

Por que a aplicação recusa novas conexões com o servidor tranquilo?

Provavelmente o limite de descritores do sistema operacional foi atingido. O sintoma é característico: a aplicação aceita conexões normalmente até um número específico e sempre o mesmo, e então passa a recusar todas as novas, sem saturação de memória ou processador.

Como escalar uma aplicação de tempo real para vários servidores?

É preciso um mecanismo que distribua as mensagens entre os processos, porque cada um conhece apenas as próprias conexões. Sem isso, um usuário conectado a um processo não recebe mensagens originadas em outro. Esse componente adicional precisa ser monitorado e mantido.

O que acontece com as conexões quando publico uma nova versão?

Todas caem ao mesmo tempo, e os clientes tentam reconectar. Se todos tentarem simultaneamente, produzem uma rajada que pode impedir a aplicação de subir. Reconexão com espera crescente e variação aleatória no cliente é a medida mais eficaz, junto com publicação gradual dos processos.

Preciso mesmo de tempo real?

A pergunta que orienta é se um atraso de alguns segundos prejudicaria a experiência. Para muitos casos, uma consulta periódica a cada dez segundos é indistinguível de tempo real para o usuário e dispensa toda a complexidade — proxy ajustado, mecanismo entre processos e reconexão elaborada.

O que monitorar em uma aplicação com conexões persistentes?

Número de conexões abertas por processo e no total, taxa de conexão e desconexão, equilíbrio entre processos, memória correlacionada ao número de conexões, tempo de entrega das mensagens e a proximidade do limite de descritores — que permite agir antes de as recusas começarem.

Facebook
X
LinkedIn