...

Otimização do estado TIME_WAIT do TCP em servidores web: guia prático para administradores

Mostro como TCP TIME_WAIT nos servidores Web de forma a que uma carga elevada de curta duração não esgote as portas e as novas ligações sejam estabelecidas rapidamente. O guia prático fornece pontos de medição claros, opções seguras do kernel, otimização de sockets orientada para as aplicações e técnicas de arquitetura que mantêm o TIME_WAIT como uma rede de segurança útil e, ao mesmo tempo, aumentam a taxa de transferência.

Pontos centrais

Os aspetos fundamentais que se seguem orientam de forma específica a análise e a otimização do TIME_WAIT em servidores web Linux.

  • Compreensão: O TIME_WAIT protege a integridade dos dados; o objetivo é o controlo, e não o desligamento.
  • Feiras: Registar com precisão a percentagem de TIME_WAIT, a utilização das portas e as taxas de reconexão.
  • Kernel: ip_local_port_range, tcp_fin_timeout, tcp_tw_reuse – ajustar com cuidado e de forma mensurável.
  • Soquetes: O Keep-Alive, o HTTP/2/3 e os conjuntos de ligações reduzem a rotatividade das ligações.
  • Arquitetura: A escalabilidade, os IPs/portas adicionais e os proxies distribuem a carga TIME_WAIT.

Compreender corretamente o estado TIME_WAIT

Muitos administradores observam milhares de ligações em TIME_WAIT e pensamos que se trata de um erro, mas acontece exatamente o contrário. O sistema mantém as ligações encerradas por um breve período, para que os segmentos que chegam mais tarde não interfiram com novas ligações e todos os bytes cheguem ao seu destinatário. Respeito esta lógica de segurança, pois evita a mistura de dados e os irritantes RSTs. Em servidores web muito movimentados, o número de sockets de curta duração aumenta naturalmente, o que exige uma avaliação ponderada e não uma reação de pânico. O que é decisivo é verificar se existe realmente escassez de portas, transbordamentos de backlog ou erros dos utilizadores antes de iniciar o ajuste.

Detetar sintomas em servidores com elevada carga de trabalho

Primeiro verifico o Porto-Mensagens de erro: „Cannot assign requested address“ ou „Address already in use“ indicam esgotamento. Handshakes atrasados, rejeições esporádicas e picos de utilização da CPU do kernel no caminho de rede são outros sinais de alerta. Quando a monitorização revela um número invulgarmente elevado de sockets TIME_WAIT, comparo sempre esse número com as taxas de novas ligações e os tempos de resposta. Uma elevada percentagem de TIME_WAIT, por si só, continua a ser tolerável, desde que as portas efémeras livres e as tabelas de sockets ofereçam margem suficiente. Só quando surgem estrangulamentos concretos é que decido ajustar os parâmetros de forma específica, em vez de agir com base em suspeitas.

Medir e avaliar: visão geral dos estados e das portas

Sem números, não consigo otimizar nada, por isso começo por ss e o Netstat, para registar a distribuição dos estados e as tendências. Além disso, analiso o diretório /proc/net/tcp, pois aí estão disponíveis detalhes sobre portas locais/remotas e estados. A partir da monitorização, extraio os contadores de TIME_WAIT por anfitrião, novas ligações por segundo e taxas de erro por minuto. Interesso-me pela relação entre o TIME_WAIT e o total de sockets, bem como pela utilização das portas efémeras, para distinguir a pressão real do que é meramente aparente. Só quando estas métricas confirmarem a existência de estrangulamentos é que planeio medidas concretas ao nível do kernel e das aplicações.

Ajuste do kernel: ajustes seguros com bom senso

Começo por conservador Efetue alterações e implemente-as gradualmente, sempre acompanhadas de medições e de uma opção de reversão. Um valor mais elevado para `ip_local_port_range` aumenta a seleção de portas de origem, o que reduz as colisões de portas. Uma redução cautelosa do `tcp_fin_timeout` encurta determinados estados finais, sem correr o risco de interrupções prematuras. Em configurações sem NAT, o parâmetro tcp_tw_reuse pode reduzir significativamente a pressão nas portas, desde que eu conheça bem o ambiente e os testes decorram sem problemas. Um valor suficientemente elevado para tcp_max_tw_buckets evita o descarte agressivo, mas deve estar de acordo com a capacidade de RAM disponível.

Parâmetros Objetivo Exemplo de valor Risco Variável medida
net.ipv4.ip_local_port_range Ampliar o conjunto de portas efémeras 12000 65535 Mais abertas Portos consomem recursos do kernel Portas livres, erros de ligação
net.ipv4.tcp_fin_timeout Reduzir a duração das fases FIN 30–45 segundos Valores demasiado baixos favorecem as interrupções de gravidez Retransmissões, quota RST
net.ipv4.tcp_tw_reuse Reutilizar sockets TIME_WAIT 1 (seletivo) É arriscado em ambientes NAT Proporção de TIME_WAIT, taxas de erro
net.ipv4.tcp_max_tw_buckets Número máximo de sockets TIME_WAIT Valor elevado e adequado Se for demasiado pequeno, provoca deformações Kernel drops, RSTs
opções obsoletas (por exemplo, tcp_tw_recycle) Comportamentos antigos e problemáticos Manter desativado Bloqueios no NAT e erros legítimos de ligação Acumulação de erros, reclamações dos clientes

Boas práticas para alterações na pilha de rede

Alterarei apenas alguns por cada passo Parâmetros, para que eu possa atribuir de forma clara a relação de causa e efeito. Primeiro, defino objetivos claros, como evitar o esgotamento de portas, valores aceitáveis de TIME_WAIT e valores de latência constantes. Cada alteração é inicialmente testada em sistemas de teste com padrões de carga realistas e planos de reversão controlados. Durante a implementação, correlaciono as métricas de rede e de aplicação, porque só a interação entre ambas reflete a experiência do utilizador. Só quando os valores medidos se revelam convincentes ao longo de várias fases de carga é que aplico as configurações de forma permanente.

Otimização de sockets ao nível da aplicação

Muitas vezes, é através de isso que consigo o maior alívio Manter em permanência e a reutilização de ligações, porque menos novas ligações também geram menos TIME_WAIT. Ativo o HTTP Keep-Alive e defino tempos de inatividade adequados, para que um número reduzido de ligações de longa duração suporte muitas solicitações. Sempre que possível, utilizo o HTTP/2 ou o HTTP/3 para multiplexar várias solicitações através de um número reduzido de ligações. Para os clientes de backend, trabalho com conjuntos de ligações que mantêm as ligações abertas e as renovam cuidadosamente. A minha referência a HTTP Keep-Alive, que utilizo sistematicamente para serviços web.

Decisões de arquitetura que atenuam o TIME_WAIT

Distribuo a carga horizontalmente, para que TIME_WAIT não se concentrem num único host e as portas comecem a escassear. Mais endereços IP ou portas de lista adicionais aumentam o número de combinações possíveis de origem/destino e reduzem as colisões. Os proxies inversos à frente do origin agrupam as ligações dos clientes e comunicam internamente de forma eficiente com os back-ends agrupados. Continua a ser fundamental definir valores de timeout coordenados, para que os proxies, os balanceadores de carga e os backends não interrompam as ligações prematuramente. Quem utiliza o Apache deve Tempo limite de Keep-Alive ajustar cuidadosamente aos padrões de tráfego e às latências.

Escolha de alojamento e servidor tendo em conta o TIME_WAIT

Prefiro fornecedores com Linux-Kernel, porque as funcionalidades TCP modernas facilitam o dia-a-dia. O controlo granular dos parâmetros sysctl poupa tempo na análise e na implementação. A monitorização integrada do estado da rede e das sockets acelera a avaliação após alterações. Para serviços com muitas ligações de curta duração, vale a pena investir em hardware de alto desempenho e numa rede capaz de lidar com picos de carga com facilidade. Desta forma, não só implemento otimizações TIME_WAIT, como também as mantenho a funcionar de forma fiável em produção.

Guia prático: Servidor API sob carga de curta duração

Começo por dar uma volta de medição e registo Novas ligações por segundo, a percentagem de TIME_WAIT e as taxas de erro. Em seguida, aumento o intervalo de ip_local_port_range e reduzo cuidadosamente o tcp_fin_timeout, enquanto observo as retransmissões. Num ambiente sem NAT, ativo o tcp_tw_reuse a título de teste, registo os resultados e reajo imediatamente caso detecte anomalias. Paralelamente, certifico-me de que o Keep-Alive está ativo, que o HTTP/2 está a funcionar e que a aplicação utiliza corretamente os conjuntos de ligações. Por fim, analiso as tendências do TIME_WAIT ao longo de várias fases de pico, antes de definir as configurações de forma definitiva.

Monitorização e funcionamento contínuo

Eu registo cada Alteração com o valor inicial, o objetivo e o efeito observado, para que eu possa verificar rapidamente mais tarde. Os processos de mudança com uma estratégia clara de reversão protegem contra danos a longo prazo em caso de erros. Além do TIME_WAIT, conto o RTT, as retransmissões, o goodput e as taxas de erro, para ter uma visão completa da experiência do utilizador. Para ligações de backend de longa duração, considero TCP Keepalive de forma consistente, para que as ligações obsoletas desapareçam e os recursos fiquem disponíveis. É assim que acompanho as otimizações no dia a dia, em vez de as considerar uma ação pontual.

Quem assume o TIME_WAIT? Encerramento ativo vs. passivo

Avalio sempre qual das partes encerra ativamente a ligação, pois a parte que a encerra ativamente acaba normalmente em TIME_WAIT. Nos clientes Web clássicos, o cliente fecha frequentemente, pelo que o servidor regista menos TIME_WAIT – nas chamadas de backend, por outro lado, a minha aplicação é ela própria o cliente e acumula TIME_WAIT. Evito o encerramento ativo forçado no servidor (por exemplo, SO_LINGER=0), porque isso pode provocar RSTs e causar perda de dados. Em vez disso, opto por encerramento elegante, defina tempos de espera de keep-alive adequados e, sempre que possível, deixe que seja o cliente a encerrar a ligação primeiro. Isto não só diminui o estado TIME_WAIT no servidor, como também reduz as situações de erro causadas por encerramentos prematuros. Quando estabeleço muitas ligações de saída (por exemplo, para bases de dados ou servidores a montante), uma boa reutilização de ligações tem um efeito mais imediato do que qualquer ajuste do kernel.

Dimensionar corretamente as filas de lista e de aceitação

Asseguro-me de que as ligações recebidas não falhem antes mesmo de chegarem à aplicação. Para tal, ajusto net.core.somaxconn e os valores de backlog do meu servidor web, para que a fila de aceitação não fique sobrecarregada. net.ipv4.tcp_max_syn_backlog Defino o tamanho de acordo com o pico dos handshakes recebidos; valores demasiado baixos provocam perdas já na fase SYN. tcp_syncookies Mantenho esta opção ativada para garantir a estabilidade em picos de tráfego de curta duração, mas verifico, através de testes de carga, se o tráfego legítimo não fica a ser abrandado. Quando utilizo vários workers, defino SO_REUSEPORT, para distribuir a carga de forma uniforme pelos núcleos da CPU e reduzir a contenção do Accept Lock. Estas medidas não resolvem a escassez de portas, mas evitam interpretações erradas, caso as rejeições sejam erroneamente atribuídas ao estado TIME_WAIT.

Manter-se atento ao NAT, ao balanceador de carga e ao Conntrack

Faço uma distinção clara entre problemas do servidor anfitrião e problemas de perímetro. Por trás de um SNAT ou Cloud-NAT, não só o servidor, mas também o gateway NAT com os seus de saída As portas efémeras tornam-se um gargalo. Nesses cenários, resolvo a pressão através de IPs de saída adicionais, uma distribuição mais precisa das portas ou taxas de reconexão mais baixas através de pools. Nos edge de Linux, verifico nf_conntrack_max e os tempos de espera TCP no Conntrack; manter durante demasiado tempo o rastreio próximo do estado TIME_WAIT consome memória e pode suplantar fluxos legítimos. Reduzo os tempos de espera do Conntrack apenas com cautela e sempre em conjunto com os valores da aplicação e do kernel, para não cortar segmentos tardios. Importante: tcp_tw_reuse aplica-se exclusivamente às ligações de saída do anfitrião, e não às ligações de entrada no ouvinte, e define tcp_timestamps=1 Por isso, testo os ambientes NAT de forma particularmente minuciosa.

HTTP/3 e UDP: o que muda?

Com o HTTP/3, o protocolo de transporte passa a ser QUIC/UDP, o que elimina o clássico TCP-TIME_WAIT. Por isso, planeio de outra forma: em vez dos estados TCP, observo o número de sockets UDP, a utilização das portas efémeras e as entradas do Conntrack para UDP. O QUIC reduz significativamente os custos de estabelecimento de ligações e diminui a rotatividade das ligações, mas exige tempos de espera em inatividade consistentes entre o cliente, o proxy e a origem. Em ambientes mistos (H2/H3), certifico-me de que as políticas de Keep-Alive se mantêm coerentes, para que as vantagens do multiplexamento não sejam desperdiçadas por tempos de inatividade demasiado curtos.

Limites de recursos e restrições do sistema operativo

Começo por estabelecer uma base sólida Limites dos descritores de ficheiros um (ulimit nofile, fs.file‑max, fs.nr_open), pois limites demasiado restritos geram erros secundários que o TIME_WAIT apenas mascara. Os limites de memória TCP (net.ipv4.tcp_mem, tcp_rmem, tcp_wmem) ajusto-o de forma a que a pilha não fique sob pressão de memória quando há muitas ligações simultâneas. Para portas de serviço bem separadas, considero que ip_local_reserved_ports atualizado, para que as portas efémeras não entrem acidentalmente em conflito com as portas do servidor. Nos testes de carga, verifico se o crescimento do slab (por exemplo, para blocos de controlo TCP) se mantém estável — só assim consigo avaliar se um valor mais elevado de tcp_max_tw_buckets é realmente viável.

Características específicas dos contentores e do Kubernetes

Nos contentores, tenho em conta que as faixas de portas efémeras, os ulimits e os sysctls por Espaço de nome podem variar. As redes de serviços (service meshes) e os sidecars duplicam frequentemente o número de ligações (Cliente↔Sidecar↔Proxy↔Backend) e, consequentemente, o potencial para TIME_WAIT – é aqui que obtenho os maiores ganhos através da reutilização de ligações e de temporizadores de inatividade ajustados. Os NodePorts e o SNAT nos workers sobrecarregam adicionalmente as tabelas de conntrack; observo estes valores separadamente do host do pod. Sob carga, distribuo o tráfego de saída por vários nós ou utilizo gateways de saída dedicados para evitar pontos de congestionamento nas portas. É importante ter em conta que, se eu otimizar no pod, a rede do host (incluindo NAT/Conntrack) tem de estar em sintonia com isso; caso contrário, estou apenas a deslocar o problema.

Manual de diagnóstico e valores de referência úteis

Para avaliar rapidamente a situação, sigo uma sequência fixa: em primeiro lugar, ss -s e ss -tan estado time-wait quanto à ordem de grandeza, em segundo lugar /proc/sys/net/ipv4/ip_local_port_range verificar e estimar as portas efémeras livres; em terceiro lugar, verificar as mensagens de erro e as taxas de RST nos registos da aplicação e do kernel. Depois, medo as novas ligações por segundo e correlaciono-as com as latências. Como valores de referência, tolero proporções elevadas de TIME_WAIT, desde que: não ocorra esgotamento de portas, nenhuma fila de aceitação transborde, as retransmissões se mantenham estáveis e os tempos de resposta não sofram desvios. Só considero uma otimização „concluída“ quando os mesmos picos de carga forem reproduzíveis ao longo de vários dias sem anomalias.

Erros comuns e anti-padrões

Evito cortes generalizados de TIME_WAIT, pois assim corro o risco de misturar dados e de ocorrerem erros esporádicos. A redução cega dos tempos de espera penaliza os utilizadores com interrupções de ligação em situações de carga elevada. Não altero opções desatualizadas como a tcp_tw_recycle, porque podem interromper acessos legítimos. O ajuste puro do kernel, sem trabalho ao nível das aplicações e da arquitetura, pouco adianta se surgirem demasiadas ligações de curta duração. Quem altera tudo ao mesmo tempo dificulta uma análise clara das causas e prolonga a deteção de erros.

Resumo compacto para administradores

Eu trato TIME_WAIT Como mecanismo de proteção, começo por fazer medições precisas e, só depois, vou otimizando gradualmente. Consigo o maior impacto com a reutilização de ligações através de Keep-Alive, HTTP/2/3 e pools, acompanhada por ajustes cautelosos no sysctl. Elementos de suporte da arquitetura, como IPs adicionais, proxies e escalabilidade horizontal, distribuem eficazmente a carga de ligações. A monitorização contínua, a documentação rigorosa e os objetivos claros garantem latências constantes e portas disponíveis. Desta forma, o servidor web mantém-se ágil mesmo com tráfego elevado, enquanto o TIME_WAIT funciona de forma controlada e previsível.

Artigos actuais

Servidor Linux com indicadores visualizados de «Pressure Stall Information» no centro de dados
Administração

Linux PSI para uma análise e monitorização precisas do desempenho

O Linux PSI (Pressure Stall Information) mostra em que medida a CPU, a memória e as E/S estão a limitar o desempenho do teu sistema. Descobre como ativar o PSI e como utilizá-lo para uma monitorização precisa do desempenho.