Aplicações Node.js fazem milhares de chamadas HTTP para APIs, bancos compatíveis com HTTP, serviços internos e provedores externos. Abrir uma nova conexão TCP para cada requisição aumenta latência, consumo de CPU e uso de portas efêmeras. O HTTP Agent no Node.js gerencia conexões reutilizáveis, filas e sockets disponíveis para reduzir esse custo.
O Agent participa das APIs tradicionais de node:http e node:https. Ele decide quando reutilizar um socket, quando abrir outro, quantas conexões simultâneas manter e por quanto tempo sockets ociosos permanecem disponíveis. Configurações inadequadas podem causar filas, resets, excesso de conexões ou esgotamento de recursos.
Neste guia, você aprenderá a usar keep-alive, configurar limites, observar sockets, separar pools por destino, lidar com HTTPS, proxies, timeouts, shutdown e testes de carga.
O que é um HTTP Agent?
O Agent é um gerenciador de conexões usado pelo cliente HTTP tradicional do Node.js. A documentação oficial de http.Agent explica propriedades, eventos e opções. Para TLS, consulte também a documentação oficial de HTTPS.
Para revisar o protocolo, veja HTTP/2 no Node.js. Para conexões seguras, consulte HTTPS e TLS no Node.js. O artigo TCP com Net no Node.js ajuda a compreender o socket subjacente.
Requisição com Agent padrão
const http = require('node:http');
http.get('http://example.com/data', response => {
response.resume();
});Quando nenhum Agent é informado, a API usa o Agent global correspondente. O comportamento exato de reutilização depende da versão e das opções.
Criando um Agent com keep-alive
const http = require('node:http');
const agent = new http.Agent({
keepAlive: true,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000
});keepAlive permite manter sockets após uma resposta para reutilização futura. Isso evita novo handshake TCP em chamadas seguintes ao mesmo destino.
Usando o Agent
const request = http.get({
hostname: 'api.example.com',
path: '/users',
agent
}, response => {
response.resume();
});
request.on('error', console.error);O pool é organizado por destino e opções relevantes. Conexões de hosts ou portas diferentes não são misturadas.
HTTPS Agent
const https = require('node:https');
const agent = new https.Agent({
keepAlive: true,
maxSockets: 30,
maxFreeSockets: 10,
rejectUnauthorized: true
});Em HTTPS, a reutilização economiza também parte do custo de TLS. Nunca desative a validação de certificados em produção para contornar erro de configuração.
maxSockets
maxSockets limita conexões ativas por origem. Um valor muito baixo cria fila e aumenta latência. Um valor muito alto pode sobrecarregar o serviço remoto e esgotar portas locais.
Escolha o limite usando carga, tempo médio de resposta e capacidade do destino. Comece com um valor conservador e meça.
maxTotalSockets
Versões modernas podem oferecer maxTotalSockets, que limita o total de sockets do Agent. Isso é útil quando a aplicação chama muitos hosts.
Sem limite global, dezenas de destinos podem multiplicar conexões apesar do limite individual.
maxFreeSockets
Define quantos sockets ociosos ficam guardados para reutilização. Um valor alto reduz handshakes em tráfego intermitente, mas mantém descritores e memória ocupados.
Scheduling
Algumas versões permitem escolher política de reutilização, como LIFO ou FIFO. LIFO tende a reutilizar sockets mais recentes, reduzindo chance de selecionar uma conexão ociosa encerrada pelo servidor. FIFO pode distribuir uso entre mais sockets.
Teste conforme o timeout de keep-alive do destino.
Keep-Alive HTTP
Keep-alive não significa que a conexão viverá para sempre. Servidores, proxies e balanceadores encerram sockets após períodos de inatividade. O cliente precisa tratar ECONNRESET e falhas de reutilização.
Timeout do socket
const agent = new http.Agent({
keepAlive: true,
timeout: 20000
});O significado de opções de timeout pode variar entre socket ativo, ocioso e requisição. Não dependa de um único timeout para toda a operação.
Timeout total da requisição
const request = http.get(options);
request.setTimeout(5000, () => {
request.destroy(new Error('Tempo limite excedido'));
});Defina prazo de conexão, resposta inicial e duração total conforme a biblioteca usada. Veja AbortController no Node.js para cancelamento coordenado.
Filas internas
Quando todos os sockets permitidos estão ocupados, novas requisições aguardam no Agent. A fila não deve crescer sem limite. A aplicação precisa limitar concorrência antes de criar requisições.
const limit = createConcurrencyLimit(40);
await Promise.all(items.map(item =>
limit(() => callRemoteService(item))
));Observando sockets
function getAgentStats(agent) {
return {
activeOrigins: Object.keys(agent.sockets).length,
freeOrigins: Object.keys(agent.freeSockets).length,
queuedOrigins: Object.keys(agent.requests).length
};
}Essas estruturas são úteis para diagnóstico, mas são detalhes da API e podem mudar. Não exponha hosts internos sem sanitização.
Eventos de socket
request.on('socket', socket => {
socket.on('connect', () => {
logger.debug('tcp_connected');
});
socket.on('secureConnect', () => {
logger.debug('tls_connected');
});
});Use instrumentação amostral para não gerar logs excessivos.
Separando Agents por serviço
Um único Agent global pode misturar políticas de serviços diferentes. Crie pools separados para destinos críticos:
const paymentAgent = new https.Agent({
keepAlive: true,
maxSockets: 20
});
const analyticsAgent = new https.Agent({
keepAlive: true,
maxSockets: 5
});Assim, um serviço lento não consome toda a capacidade destinada a outro.
DNS e conexões
O Agent reutiliza sockets já abertos, mas novas conexões dependem de DNS. Mudanças de IP podem demorar a aparecer enquanto conexões antigas continuam vivas.
Consulte DNS no Node.js para resolução, cache e famílias IPv4 ou IPv6.
Proxy
Quando há proxy HTTP, o destino do socket pode ser o próprio proxy. Para HTTPS via CONNECT, bibliotecas especializadas costumam fornecer Agents próprios.
Não aceite endereço de proxy arbitrário de entrada externa. Isso pode permitir SSRF ou acesso à rede interna.
Certificados de cliente
const agent = new https.Agent({
keepAlive: true,
cert: clientCertificate,
key: clientKey,
ca: trustedCa
});Agents com credenciais diferentes não devem ser compartilhados. Proteja chaves e evite registrá-las.
SNI e sessões TLS
O HTTPS Agent considera opções de conexão e servidor. Sessões TLS podem ser reutilizadas conforme o runtime e o servidor, reduzindo custo de handshakes futuros.
Retries
Uma falha de socket não significa que toda requisição pode ser repetida. GETs idempotentes geralmente são mais seguros; POSTs podem duplicar efeitos.
Veja Retry com Backoff no Node.js para jitter, limites e idempotência.
Graceful shutdown
async function shutdown() {
agent.destroy();
}destroy() encerra sockets controlados pelo Agent. Pare de aceitar novos trabalhos, aguarde requisições em andamento e só então destrua o pool.
Consulte Graceful Shutdown no Node.js.
Agent false
http.get({
hostname: 'example.com',
agent: false
});Isso cria uma conexão sem usar pool compartilhado. Pode ser útil em casos isolados, mas aumenta custo se usado em alto volume.
Fetch e Agent
O fetch() nativo usa a implementação baseada em Undici e não é configurado diretamente com http.Agent. Ele possui conceitos próprios de dispatcher e pool.
Não tente passar um Agent tradicional esperando o mesmo comportamento. Consulte o artigo Fetch Nativo no Node.js quando disponível.
HTTP/2
HTTP/2 multiplexa várias requisições em uma conexão e não usa o mesmo modelo de http.Agent. Escolha o protocolo conforme suporte do serviço e características da carga.
Testes de carga
Compare:
- latência p50, p95 e p99;
- conexões abertas;
- taxa de resets;
- tempo em fila;
- uso de CPU;
- portas efêmeras;
- erros do serviço remoto.
Teste com tráfego realista e duração suficiente para observar sockets ociosos.
Erros comuns
- Criar Agent por requisição: nenhuma conexão é reutilizada.
- maxSockets ilimitado: o destino pode ser sobrecarregado.
- Fila sem limite: memória e latência aumentam.
- Ignorar resets: conexões fechadas pelo servidor causam falhas.
- Compartilhar credenciais: serviços usam certificados incorretos.
- Não destruir no shutdown: o processo demora a encerrar.
- Usar Agent de HTTP no fetch: a configuração não tem efeito esperado.
Boas práticas
- Reutilize um Agent por política e destino.
- Ative keep-alive em cargas recorrentes.
- Defina limites de sockets.
- Limite concorrência antes do Agent.
- Configure timeouts em várias etapas.
- Monitore filas e resets.
- Separe serviços críticos.
- Use retries apenas quando seguros.
- Teste DNS, proxy e TLS.
- Destrua pools durante shutdown.
Conclusão
O HTTP Agent no Node.js reduz o custo de conexões ao manter pools, reutilizar sockets e limitar concorrência por origem. Ele é essencial em clientes que fazem chamadas frequentes com node:http ou node:https.
Resultados consistentes dependem de limites, timeouts, observabilidade e tratamento de conexões encerradas. Ao separar pools por serviço e medir filas, sockets e latência, a aplicação consegue equilibrar desempenho com proteção do próprio processo e dos sistemas remotos.


