Com o foco em Tempo limite do HTTP Keep-Alive Vou mostrar-lhe como definir tempos de inatividade para que as ligações sejam reutilizadas sem bloquear threads. Explico valores específicos, mostro armadilhas típicas e forneço configurações testadas e comprovadas para nginx, Apache e o sistema operativo.
Pontos centrais
- EquilíbrioO que é que isso quer dizer?.
- ValoresNa maioria dos casos, 5-15 s e 100-500 pedidos por ligação.
- CoordenaçãoCoordenar os tempos limite do cliente, do LB e da firewall.
- Casos especiaisWebSockets, SSE, Polling longo separadamente.
- MonitorizaçãoMonitorar sockets abertos, FDs e latências.
Breve explicação do HTTP Keep-Alive
Mantenho ligações TCP com Manter em permanência aberto para que vários pedidos utilizem a mesma linha. Isto poupa os repetidos handshakes TCP e TLS e reduz o CPU-superfície de forma notável. Isto é particularmente benéfico para muitos ficheiros pequenos, como ícones, JSON ou CSS. Cada nova ligação evitada reduz as trocas de contexto e alivia as rotinas do kernel. Em benchmarks com uma elevada proporção de GET, a duração total é significativamente reduzida porque são gerados menos pacotes SYN/ACK e mais tempo de computação flui para a lógica da aplicação.
Meço rapidamente o efeito: as latências médias móveis tornam-se mais suaves e o número de novas ligações TCP por segundo diminui. Não consigo isso por mágica, mas por Reutilização de ligações e limites sensatos. Continua a ser importante que o Keep-Alive não substitua a renderização rápida ou o armazenamento em cache. Reduz os tempos de espera na fronteira da rede, enquanto a própria aplicação deve continuar a responder de forma eficiente. Ambos juntos aumentam o Desempenho visivelmente.
Compreender o tempo limite correto
O tempo limite define o tempo que uma ligação inativa permanece aberta antes de o servidor a fechar. encerra. Se o definir demasiado curto, os clientes estão constantemente a abrir novas ligações TCP, o que Despesas gerais é aumentado. Se o definir durante demasiado tempo, as ligações inactivas estacionam trabalhadores ou threads preciosos. A arte reside no equilíbrio entre a reutilização e o consumo de recursos. Faço testes práticos: primeiro defino-o de forma aproximada, depois afino-o com testes de carga.
Também presto atenção à relação entre os tempos de resposta e as janelas de inatividade. Se a interação típica do utilizador entre dois cliques for de 2-4 segundos, um timeout de 5-15 segundos cobre normalmente o padrão real. As chamadas API curtas podem facilmente tolerar 5-10 segundos, as cargas de trabalho multimédia 10-15 segundos. É importante não exagerar: timeouts demasiado longos raramente levam a mais Rendimento, mas frequentemente conduzem a bloqueios Recursos. Reconheço-o rapidamente pelo número crescente de tomadas abertas e pelos números elevados de FD.
Separar os tipos de tempo limite de forma clara
Faço uma distinção rigorosa entre Tempo limite de inatividade (Keep-Alive), Tempo limite de leitura/cabeçalho (quanto tempo o servidor espera pelos pedidos recebidos) e Tempo limite de envio/escrita (quanto tempo é tolerado o envio para o cliente). Estas categorias desempenham diferentes funções:
- Tempo limite de inatividade: Controla a reutilização e a duração do estacionamento das ligações inactivas.
- Tempo limite de leitura/cabeçalho: Protege contra clientes lentos (slow lorises) e cabeçalhos enviados pela metade.
- Tempo limite de envio/escrita: Evita que o servidor espere infinitamente por uma receção lenta no cliente.
Em nginx Utilizo deliberadamente header_timeout/read_timeout e send_timeout por contexto (http/server/location), para além de keepalive_timeout. Desde versões mais recentes, eu opcionalmente defino tempo de espera, para limitar o tempo de vida máximo de uma ligação, mesmo que esta se mantenha ativa. Em Apache Também utilizo RequestReadTimeout (mod_reqtimeout) e verificar Tempo limite (global) separado de Tempo de espera de manutenção de conexão. Esta separação é um elemento importante para evitar a imobilização de recursos sem qualquer benefício real.
Valores recomendados na prática
Para ambientes produtivos, defino um tempo de espera de 5-15 segundos e 100-500 pedidos por ligação. Esse intervalo atinge boas taxas de reutilização de conexão e mantém baixo o número de conexões inativas. Em nginx Utilizo keepalive_timeout 10s como valor inicial e keepalive_requests 200. Se houver muito tráfego, aumento-o moderadamente se vir demasiadas ligações TCP novas. Se o tráfego for escasso, reduzo-o novamente para evitar uma inundação de tráfego inativo.
Aqueles que se aprofundam beneficiarão de um processo de afinação claro com pontos de medição. Para tal, resumi as minhas orientações num guia prático que descreve o caminho da medição à configuração e ao controlo. Para um início rápido, remeto-o para os meus passos em Ajuste do Keep Alive. Como controlar Reutilização e limites e evitar surpresas. No final, o que conta é a baixa latência com Rendimento.
Riscos de longos períodos de espera
Um tempo limite longo mantém as ligações artificiais aberto e bloqueia os trabalhadores mesmo que não haja nenhum pedido a seguir. Isso faz com que os soquetes inchem e aumenta o número de descritores de arquivo. Se o processo atingir os seus limites, vejo erros de aceitação de rejeição ou filas de espera ao estabelecer uma ligação. A memória cresce, os colectores de lixo ou os alocadores custam tempo adicional e a latência aumenta. No caso de um erro, os clientes enviam para sockets que já estão fechados e recebem Erro.
Evito-o definindo valores moderados e verificando regularmente as métricas. Se as ligações inactivas aumentarem demasiado com pouca carga, reduzo o tempo limite. Se vejo muitas ligações novas por segundo durante os picos de tráfego, aumento-o cuidadosamente em pequenos passos. É assim que mantenho o Capacidade utilizável e evitar ligações mortas. O resultado é um sistema mais suave com menos Dicas nas curvas.
Configuração: nginx, Apache e camada do SO
Começo ao nível do servidor Web e defino o tempo limite e os limites. Em nginx Eu defino keepalive_timeout 5-15s e keepalive_requests 100-500. No Apache com event-MPM eu combino KeepAlive On, KeepAliveTimeout 5-15 e MaxKeepAliveRequests 100-500. Então eu calibro os pools de workers ou threads de acordo com a carga esperada. Isso evita que keep-alives ociosos se tornem produtivos. Caça-níqueis ligar.
Aumento os limites e as filas ao nível do sistema operativo. Defino ulimit -n para pelo menos 100.000, ajusto net.core.somaxconn e tcp_max_syn_backlog e verifico o tratamento de TIME_WAIT. Isso garante que o kernel e o processo tenham Recursos fornecer. Finalmente, verifico os caminhos da placa de rede através do balanceamento de IRQ para a aplicação. Isto permite-me reconhecer atempadamente os estrangulamentos e manter o Latência baixo.
| Componente | Diretiva/Definição | Recomendação | Nota |
|---|---|---|---|
| nginx | tempo de espera de keepalive | 5–15 s | Mais curto com pouco tráfego, mais tempo com muitos pedidos pequenos |
| nginx | keepalive_requests | 100–500 | Recicla compostos e reduz Fugas |
| Apache (evento) | Tempo de espera de manutenção de conexão | 5–15 s | O Event-MPM gere os inactivos de forma mais eficiente do que o prefork |
| Sistema operativo | ulimit -n | ≥ 100.000 | Mais FDs abertos para muitos Soquetes |
| Sistema operativo | net.core.somaxconn | Aumentar | Menos ligações rejeitadas sob Carga de pico |
Proxy invertido e reutilização a montante
Penso sempre em "keep-alive de ponta a ponta. Por trás do servidor de borda, muitas vezes há uma cadeia de proxy reverso → servidores de aplicativos. Para o nginx, eu ativo meu próprio Piscinas Keep Alive (upstream keepalive, keepalive_requests, keepalive_timeout), definir proxy_http_version 1.1 e remover „Connection: close“. Isto também me poupa interno handshakes e descarregar backends de aplicações (Node.js, Java, PHP-FPM). No Apache com mod_proxy, também mantenho ligações persistentes aos servidores backend e limito-as por destino para que um hotspot não monopolize os pools.
Meço separadamente: taxa de reutilização Cliente→Borda e Borda→Backend. Se eu vir uma boa reutilização na borda, mas muitas conexões novas para o backend, eu aumento seletivamente os pools upstream. Isso me permite escalar sem aumentar globalmente os tempos limite do frontend.
Trabalhadores, threads e limites do SO
Não dimensiono trabalhadores, eventos e tópicos de acordo com os valores pretendidos, mas sim de acordo com perfil de carga. Para tal, monitorizo os pedidos activos, os trabalhadores inactivos, a utilização do ciclo de eventos e as mudanças de contexto. Se as threads estiverem paradas em modo ocioso, reduzo o tempo limite ou os limites máximos de ociosidade por thread. Se vejo 100% de CPU o tempo todo, verifico as filas de aceitação, a distribuição de IRQ e a pilha de rede. Pequenas correcções aos limites de FD e aos atrasos fazem frequentemente uma grande diferença. Efeitos.
Planeio a margem de manobra de forma realista. Uma reserva de 20-30 por cento em threads e FDs fornece segurança para picos. Se exagero, perco caches e o desperdício aumenta. Se não o fizer, os pedidos acabam em filas de espera ou expiram. A intersecção correta de Capacidade e a eficiência mantém as latências baixas e protege o Estabilidade.
Coordenar os tempos limite do cliente, do equilibrador de carga e da firewall
Estabeleço limites de tempo ao longo de todo o percurso para que não haja becos sem saída. Ligações são criados. O ideal é que os clientes fechem um pouco mais cedo do que o servidor. O equilibrador de carga não deve cortar mais cedo, caso contrário, verei reinicializações inesperadas. Incluo valores de inatividade de NAT e firewall para que as ligações não se percam no caminho da rede. desaparecer. Esta afinação evita as retransmissões e suaviza as curvas de carga.
Utilizo diagramas claros para manter a cadeia compreensível: Cliente → LB → servidor Web → aplicação. Documentei os tempos de inatividade, os tempos de leitura/escrita e as estratégias de repetição para cada ligação. Se eu alterar um valor, verifico os vizinhos. Isto mantém o caminho consistente e dá-me resultados de medição reproduzíveis. Esta disciplina poupa tempo na Resolução de problemas e aumenta o fiabilidade.
Segurança: Proteção contra loris lentos e abusos ociosos
Descontos de tempo abertos demasiado generosos Superfícies de ataque. Por isso, estabeleço limites que permitem uma reutilização legítima, mas que tornam mais difícil mantê-los abertos de forma maliciosa. No nginx, os limites de header e read_timeout, request_headers_size e um limite superior rígido para keepalive_requests ajudam. No Apache, eu uso mod_reqtimeout e limito conexões paralelas por IP. Limites de taxa e limite_conexão no nginx também protegem contra inundações de muitos sockets ociosos. Para pontos de extremidade de longa duração, separo pools dedicados para que os ataques a fluxos não vinculem os trabalhadores regulares da API.
Casos especiais: Long Polling, SSE e WebSockets
Os cursos de água longos chocam com os curtos Intervalos e precisam das suas próprias regras. Tecnicamente, separo estes pontos de extremidade da API clássica e das rotas de activos. Para SSE e WebSockets, defino tempos limite mais elevados, pools de trabalhadores dedicados e limites rígidos por IP. Mantenho a ligação ativa com batimentos cardíacos ou ping/pong e reconheço rapidamente as desconexões. Dessa forma, os fluxos não bloqueiam threads para Pedidos curtos.
Limito as ligações simultâneas e meço-as ativamente. Os limites demasiado elevados consomem FDs e RAM. Os limites demasiado apertados cortam o acesso a utilizadores legítimos. Eu encontro o ponto ideal com métricas limpas para conexões abertas, ociosas, ativas e descartadas. Esta separação permite-me poupar Aumentos os tempos limite e protege o Capacidade.
HTTP/2, multiplexagem e keep-alive
O HTTP/2 multiplexa vários fluxos através de um Ligação, mas continua dependente dos tempos limite. Mantenho a janela de inatividade moderada porque as sessões também podem estacionar sob HTTP/2. Os keepalive_requests elevados são menos importantes aqui, mas a reciclagem continua a ser útil. O bloqueio de cabeça de linha passa para o nível de quadro, então continuo a medir a latência por Fluxo. Se quiser fazer uma comparação mais aprofundada, encontrará informações de base em Multiplexação HTTP/2.
No HTTP/2, presto especial atenção ao número de fluxos activos por ligação. Demasiados fluxos paralelos podem sobrecarregar as threads da aplicação. Então eu diminuo os limites ou aumento os trabalhadores do servidor. O mesmo se aplica aqui: medir, ajustar, medir novamente. Isso mantém o Tempos de resposta escassos e preservados Recursos.
TLS, retoma de sessão e HTTP/3/QUIC
Os apertos de mão TLS são caros. Eu uso Reinício da sessão (bilhetes/IDs) e agrafagem OCSP, para que as reconexões sejam mais rápidas se a ligação terminar. No HTTP/3, o QUIC assume a camada de transporte: aqui o Tempo limite de inatividade QUIC semelhante ao Keep-Alive, mas numa base UDP. Também neste caso, mantenho as janelas moderadas e meço as retransmissões, uma vez que as perdas de pacotes têm um efeito diferente do que no TCP. Para ambientes mistos (H1/H2/H3), selecciono valores de referência normalizados e faço ajustes finos para cada protocolo.
Monitorização, métricas e testes de carga
Confio mais nos dados de medição do que no meu instinto e começo por uma abordagem clara KPIs. São importantes: sockets abertos, utilização do FD, novas ligações/s, latências (P50/P90/P99), taxas de erro e retransmissões. Executo perfis de carga realistas: Aquecimento, platô, rampa de descida. Em seguida, comparo as curvas antes e depois das alterações ao timeout. Um olhar sobre Fila de espera do servidor ajuda a interpretar claramente os tempos de espera.
Registo todos os ajustes com um carimbo de data/hora e os valores medidos. Isto permite-me preservar o histórico e reconhecer as correlações. Levo a sério os efeitos negativos e reverto-os rapidamente. Passos pequenos e compreensíveis poupam muito tempo. O que conta no final é um sistema estável Latência e baixo Taxa de erro sob carga.
Métodos e instrumentos de medição na prática
- Testes rápidos: Utilizo ferramentas como o wrk, ab ou vegeta para verificar as quotas de reutilização (-H connection: keep-alive vs. close), as ligações/s e os percentis de latência.
- Vista do sistema: ss/netstat mostra os estados (ESTABLISHED, TIME_WAIT), lsof -p o consumo de FD, dmesg/syslog as indicações de quedas.
- Métricas do servidor Web: O stub_status/VTS do nginx e o mod_status do Apache fornecem os dados de ativo/ inativo/espera e pedidos/s. A partir daí, posso reconhecer picos de inatividade ou estrangulamentos de trabalho.
- Traços: Utilizo o rastreio distribuído para monitorizar se os tempos de espera ocorrem no limite da rede ou na aplicação.
Configurar passo a passo
Primeiro, determino o padrão de utilização real: quantos pedidos por sessão, que Intervalos entre cliques, qual o tamanho das respostas. Em seguida, defino um perfil inicial: timeout 10 s, keepalive_requests 200, número moderado de trabalhadores. De seguida, realizo testes de carga com dados representativos. Avalio o número de novas ligações por segundo e a utilização do FD. Em seguida, ajusto o Valores em incrementos de 2-3 segundos.
Repito o ciclo até que as latências permaneçam estáveis sob carga e os picos de FD não atinjam o limite. Com tráfego intenso, só aumento o tempo limite se vir claramente menos ligações novas e os trabalhadores continuarem livres. Com baixa utilização, reduzo o tempo limite para evitar ociosidade. Em casos especiais, como o SSE, defino blocos de servidores dedicados com limites mais altos. Esse caminho leva a uma solução resiliente Definição sem cartão de taxas.
Kubernetes, contentores e escalonamento automático
Em ambientes de contentor, utilizo conetor-limites, limites de FD de pod e atrasos de nós. Garanto tempos limite de inatividade consistentes entre o Ingress, o service mesh/proxy e a aplicação. Para o escalonamento automático, presto atenção a Tempos de drenagemQuando os pods são terminados, eles devem rejeitar novas conexões via „Connection: close“ e servir as existentes de forma limpa. Os valores de keep alive que são muito longos estendem os drenos desnecessariamente, enquanto aqueles que são muito curtos geram tempestades de handshake ao escalar para fora.
Encerramento gracioso e implementações contínuas
Também tenciono desligar-me. Antes de uma implementação, reduzo gradualmente o Keep-Alive ou envio mensagens direcionadas Ligação: fechar nas respostas para que os clientes não abram novas conexões ociosas. No nginx, um worker_shutdown_timeout para os pedidos em curso. No Apache, utilizo mecanismos graciosos e mantenho um olho no MaxConnectionsPerChild/Worker para que a reciclagem ocorra automaticamente ao longo do tempo. Isso mantém as implementações suaves sem limitar os sockets abertos.
Afinação do SO: portas, tempos limite, parâmetros do kernel
- portos efémeros: Selecione um intervalo amplo para ip_local_port_range, de modo a que as ligações de curta duração não se deparem com escassez.
- TIME_WAIT: Observo os picos de TW. As pilhas modernas lidam bem com isso; evito ajustes duvidosos (tw_recycle).
- tcp_keepalive_time: Não estou a confundi-lo com o HTTP Keep-Alive. É um mecanismo do kernel para reconhecer pares mortos - útil atrás de NAT, mas não um substituto para a janela de inatividade HTTP.
- Atrasos e amortecedores: dimensionar somaxconn, tcp_max_syn_backlog e rmem/wmem de forma sensata para não estrangular sob carga.
Lista de verificação de resolução de problemas
- Muitas novas ligações/s apesar do keep-alive: Timeout demasiado curto ou clientes/LB cortados mais cedo.
- Valores elevados de inatividade e FDs completos: Timeout demasiado longo ou pools de trabalhadores demasiado grandes para o padrão de tráfego.
- Erro RST/Timeout para sessões mais longas: NAT/firewall ocioso demasiado curto no caminho, assimetria entre ligações.
- Latências de cauda longa (P99): Verifique os tempos limite de envio/leitura, os clientes lentos ou as listas de pendências demasiado preenchidas.
- Backends sobrecarregados apesar da baixa carga de borda: A gaiola a montante não existe ou é demasiado pequena.
Perfis de prática e valores iniciais
- API-first (chamadas curtas): Keep-Alive 5-10 s, keepalive_requests 200-300, timeouts apertados de cabeçalho/leitura.
- Comércio eletrónico (misto): 8-12 s, 200-400, um pouco mais generoso para imagens de produtos e visitas ao cache.
- Tipo Assets/CDN (muitos ficheiros pequenos): 10-15 s, 300-500, poças fortes a montante e limites elevados de FD.
- Intranet/baixa carga: 5-8 s, 100-200, para que o ralenti não seja dominante.
Brevemente resumido
Defino o tempo limite de HTTP keep-alive para que as ligações sejam reutilizadas sem bloquear as threads. Na prática, 5-15 segundos e 100-500 pedidos por ligação dão resultados muito bons. Coordeno os tempos limite do cliente, do equilibrador de carga e da firewall, separo as ligações de longa duração, como as WebSockets, e regulo os limites do SO. Com uma monitorização limpa, testes de carga realistas e pequenos passos, consigo obter baixos Latências e elevado Rendimento. Aqueles que mantêm esta disciplina obtêm um desempenho mensurável do hardware existente.


