Keep-Alive & Limites de Conexão
Ajuste os tempos limite de keep-alive e conexão HTTP para que os servidores Node se comportem corretamente atrás de nginx, ALB e outros proxies reversos.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain", Connection: "keep-alive" });
res.end("ok\n");
});
// Alinhe com seu proxy reverso (o keepalive_timeout padrão do nginx é 75s)
server.keepAliveTimeout = 65_000; // ligeiramente abaixo do timeout do proxy
server.headersTimeout = 66_000; // deve exceder o keepAliveTimeout
server.requestTimeout = 30_000;
server.maxConnections = 1000; // limite de sockets concorrentes
server.listen(3000);Quando usar isso: Erros intermitentes de 502 durante implantações, logs de redefinição de conexão ou EMFILE (muitos arquivos abertos) sob carga.
Exemplo de Trabalho
import { createServer } from "node:http";
import { Agent, request as httpRequest } from "node:http";
// --- Ajuste do servidor ---
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
});
server.keepAliveTimeout = 65_000;
server.headersTimeout = 66_000;
server.requestTimeout = 30_000;
server.listen(3000);
// --- Cliente de saída com reutilização de conexão ---
const agent = new Agent({
keepAlive: true,
maxSockets: 50, // conexões concorrentes por host
maxFreeSockets: 10, // sockets ociosos mantidos no pool
timeout: 30_000,
});
export function callDownstream(path: string): Promise<string> {
return new Promise((resolve, reject) => {
const req = httpRequest(
{ hostname: "api.internal", port: 8080, path, agent, timeout: 10_000 },
(res) => {
const chunks: Buffer[] = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () => resolve(Buffer.concat(chunks).toString()));
}
);
req.on("error", reject);
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
req.end();
});
}O que isso demonstra:
- Tempo limite de keep-alive do lado do servidor alinhado abaixo do proxy
headersTimeoutmaior quekeepAliveTimeout(requisito do Node)Agentde saída comkeepAlive: truepara pooling de conexões- Tempo limite por solicitação em chamadas de saída
Mergulho Profundo
Como Funciona
- HTTP/1.1 usa conexões persistentes (keep-alive) por padrão
- Após uma resposta, o socket TCP permanece aberto para reutilização
- Se o servidor e o proxy discordarem sobre quando fechar, um lado envia dados em um socket fechado (RST)
keepAliveTimeoutdo Node controla quanto tempo um socket de servidor ocioso espera pela próxima solicitação
Tabela de Alinhamento de Proxy
| Camada | Configuração | Valor Típico | Regra |
|---|---|---|---|
| nginx | keepalive_timeout | 75s | Proxy fecha o upstream ocioso |
| Servidor Node | keepAliveTimeout | 65s | Deve ser menor que o do proxy |
| Servidor Node | headersTimeout | 66s | Deve ser maior que o keepAliveTimeout |
| ALB | tempo limite ocioso | 60s padrão | Defina o keepAlive do Node abaixo do tempo limite ocioso do ALB |
| Agente de Saída | maxSockets | 50-100 | Evite que um host monopolize os FDs |
Notas de TypeScript
// Registra quando as conexões se aproximam dos limites
server.on("connection", (socket) => {
socket.setTimeout(30_000);
socket.on("timeout", () => socket.destroy());
});Armadilhas
- 502 durante implantações contínuas - o proxy envia uma solicitação para um socket que o Node já fechou. Correção: defina
keepAliveTimeout5-10s abaixo do timeout do proxy. headersTimeoutmuito baixo - Node 18+ destrói conexões no meio dos cabeçalhos. Correção:headersTimeout > keepAliveTimeout.- Sem Agente de saída - cada chamada HTTP abre uma nova conexão TCP. Correção: reutilize um
Agentde nível de módulo comkeepAlive: true. maxSocketspadrão (Infinity) - um downstream lento pode esgotar os descritores de arquivo. Correção: limitemaxSocketspor host.- Ignorando
server.maxConnections- sob ataque ou tráfego viral, a fila de aceitação cresce sem limites. Correção: definamaxConnectionse manipuleserver.on("drop"). - ULIMIT muito baixo - o padrão de 1024 FDs não é suficiente para APIs ocupadas. Correção: aumente
ulimit -nna configuração do container/systemd.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| HTTP/2 no proxy | Multiplexação reduz a contagem de conexões | Você precisa que o Node fale HTTP/2 diretamente |
| Biblioteca de pooling de conexões (undici) | Fetch moderno com pooling integrado | Já padronizado em got ou axios com agentes |
| Sockets de domínio Unix | nginx no mesmo host que o Node | Implantação entre hosts |
| Malha de serviço (Envoy) | mTLS + retentativas + quebra de circuito | Implantação simples de serviço único |
FAQs
Qual é o valor padrão do keepalive_timeout do nginx?
75 segundos. Defina o keepAliveTimeout do Node para 65s ou menos para que o Node feche primeiro, evitando condições de corrida com o proxy.
Devo desabilitar o keep-alive?
Quase nunca para APIs. Desabilitar aumenta a latência e o uso de CPU devido às apertadas de mão TCP. Considere apenas para depurar problemas específicos de conexão.
Como monitoro as conexões abertas?
server.getConnections((err, count) => ...) para a contagem ativa. Use ss -s ou Prometheus node_netstat para uma visão em todo o sistema.
O Fastify lida com isso automaticamente?
O Fastify usa o servidor Node subjacente. Você ainda define keepAliveTimeout em fastify.server após listen().
E o keep-alive do WebSocket?
WebSockets usam frames ping/pong, não keep-alive HTTP. Veja Biblioteca ws.
Quantos maxConnections devo definir?
Comece com (RPS esperado * tempo médio de resposta) * 2. Uma API de 1000 RPS com latência de 50ms precisa de aproximadamente 50-100 conexões concorrentes, mas a capacidade de pico precisa de margem.
O keep-alive afeta as verificações de integridade do balanceador de carga?
As verificações de integridade usam conexões separadas de curta duração por padrão. O keep-alive mal configurado afeta principalmente o tráfego do usuário durante as implantações.
Qual é a diferença entre keepAliveTimeout e requestTimeout?
keepAliveTimeout se aplica a sockets ociosos entre solicitações. requestTimeout limita quanto tempo uma única solicitação ativa pode ser executada.
Relacionado
- Consciência de Proxy Reverso - cabeçalhos X-Forwarded e proxy de confiança
- Considerações sobre HTTP/2 e HTTP/3 - terminação de protocolo
- Fundamentos HTTP no Node - fundamentos do servidor
- Desempenho no Express - ajuste em nível de framework
- Melhores Práticas de Fundamentos HTTP - checklist da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.