SYSTEM DESIGN · PERFORMANCE · LEITURA DE ~8 MIN
Improper
Instantiation
Uma conversa real sobre o antipadrão de recriar objetos caros — como o HttpClient — a cada requisição, e por que isso pode esgotar sockets e derrubar o throughput da aplicação.
PERGUNTA 01
MARCELO
Resume esse artigo aí — o que é esse tal de Improper Instantiation?
A RESPOSTA
É o antipadrão de arquitetura que consiste em criar repetidamente objetos caros, ou objetos que foram projetados para serem reutilizados. A ideia central é simples: se um objeto foi feito para ser compartilhado, ficar criando e destruindo novas instâncias dele a cada requisição pode destruir a performance da aplicação.
O exemplo clássico é o HttpClient. Este código parece inocente:
using (var httpClient = new HttpClient())
{
return await httpClient.GetStringAsync(url);
}
Mas em uma aplicação web isso cria um HttpClient novo a cada requisição. Sob carga alta, o servidor pode esgotar sockets e começar a gerar SocketException.
O mesmo vale pra qualquer objeto caro de inicializar, mesmo sem sockets envolvidos:
Request
↓
new ServiçoCaro()
↓
configura/conecta/inicializa
↓
usa
↓
descarta
Pagar esse custo de novo a cada requisição reduz throughput e aumenta latência. A solução é reutilizar a instância quando o objeto for compartilhável e thread-safe:
Aplicação inicia
↓
cria HttpClient UMA VEZ
↓
┌────┼────┐
↓ ↓ ↓
Req1 Req2 Req3
└────┼────┘
↓
mesma instância
Dependendo do recurso, a solução pode ser um singleton ou um pool de objetos reutilizáveis. Mas existe uma ressalva importante: isso não é "transforme tudo em singleton". O antipadrão só existe quando você recria algo que poderia ser compartilhado — e o objeto precisa ser seguro para concorrência. Recursos escassos, como conexões de banco, normalmente fazem mais sentido com pooling do que com uma instância única guardada pra sempre.
- Sintomas: queda de throughput e aumento de erros sob carga.
- Sintomas: esgotamento de sockets, conexões ou file handles.
- Sintomas: mais uso de memória e mais atividade de garbage collection.
- Sintomas: aumento de atividade de rede, disco ou banco sem motivo aparente.
Objeto caro + usado com frequência + thread-safe → pense em reutilização ou pooling, não em new a cada request.
PERGUNTA 02
MARCELO
Oxi, e qual a vantagem de fazer essa destruição do HttpClient toda hora? HttpClient é tipo um portal de comunicação com outro server, né?
A RESPOSTA
É isso mesmo: dá pra pensar no HttpClient como um canal, um portal que o código usa pra fazer requisições HTTP a outros servidores. E destruir esse canal toda hora normalmente não traz vantagem nenhuma.
O artigo não fala de "servidor 1 falando com servidor 2" — ele fala de um erro de programação: criar objetos de comunicação caros repetidamente quando eles poderiam ser reutilizados.
marcelomurilo.com (backend)
|
| HttpClient
v
api.openai.com
O problema do artigo é fazer isto sob carga:
Request 1 chega → cria HttpClient → chama API externa → destrói
Request 2 chega → cria OUTRO → chama → destrói
Request 3 chega → cria OUTRO → chama → destrói
O recomendado é mais parecido com isto — o mesmo canal fazendo várias chamadas:
marcelomurilo.com (backend)
|
v
HttpClient
único / reutilizado
/ \
v v
OpenAI API GitHub API
Vale desfazer uma confusão comum aqui: rotas como /conteudos e /en/conteudos não são "servidores separados", cada um com seu próprio HttpClient. Na prática são só rotas diferentes do mesmo site:
marcelomurilo.com
|
├── /conteudos
└── /en/conteudos
O HttpClient só entra em cena se alguma dessas rotas precisar buscar dado em outro serviço:
Usuário
↓
marcelomurilo.com/conteudos
↓
seu backend
↓
HttpClient
↓
API externa
Em uma frase: quando o programa precisa de um objeto caro como o HttpClient pra acessar outros serviços, não crie um novo a cada requisição — reutilize quando for seguro.
PERGUNTA 03
MARCELO
O que é esgotamento de sockets? Socket = meia?
A RESPOSTA
Não, socket não é meia nesse contexto. Em redes, socket é basicamente um ponto de comunicação entre dois computadores ou processos.
Seu servidor
|
| socket
v
Outro servidor
Quando o backend faz uma chamada HTTP, o sistema operacional usa sockets por baixo dos panos pra manter a comunicação de rede. E existem limites: quantos sockets ou conexões podem ficar abertos, ou em determinados estados, ao mesmo tempo.
Se o código cria e destrói HttpClient demais, acaba abrindo conexões demais em sequência. O sistema fica sem recursos disponíveis e aparecem erros como SocketException — exatamente um dos sintomas que o artigo destaca.
PERGUNTA 04
MARCELO
Entendi. Então se eu não destruo a conexão do HttpClient, quais são os trade-offs?
A RESPOSTA
Existem, sim. Compartilhar uma única instância exige que ela seja thread-safe. O HttpClient foi projetado pra esse uso, mas nem todo objeto foi.
Também pode dar problema se você ficar alterando propriedades globais desse HttpClient compartilhado enquanto várias requisições acontecem ao mesmo tempo — mudar DefaultRequestHeaders a cada request, por exemplo, pode causar condição de corrida.
E nem todo recurso deve ser mantido pra sempre. O próprio artigo cita conexões de banco: deixar uma conexão aberta sem necessidade pode impedir que outros usuários consigam usar o banco. Nesses casos, pool costuma fazer mais sentido do que uma instância única.
Então a regra não é "nunca destrua nada". É mais ou menos assim:
- Objeto barato e descartável → pode criar e destruir normalmente.
- Objeto caro, compartilhável e thread-safe → reutilize a mesma instância.
- Recurso limitado e concorrente (ex.: conexão de banco) → considere um pool.
PERGUNTA 05
MARCELO
Isso que o artigo explica era mais um hábito de programador, então?
A RESPOSTA
Sim — Improper Instantiation é essencialmente sobre um hábito comum. O raciocínio costuma ser: "criei o objeto, usei, não preciso mais, vou destruir pra liberar memória." Isso normalmente parece boa prática.
Só que, para objetos como o HttpClient, que encapsulam recursos caros e conexões externas, fazer criar → usar → destruir mil vezes pode ser muito pior do que criar uma vez → usar mil vezes.
E uma pequena correção conceitual importante: o HttpClient não é exatamente "a conexão". Ele é mais um gerenciador de comunicação HTTP que, internamente, pode abrir e reutilizar conexões e sockets:
HttpClient
↓
gerencia requisições HTTP
↓
conexões
↓
sockets
↓
rede
É exatamente essa camada de gerenciamento — e a reutilização que ela permite — que o artigo original quer preservar.
FIM · OBRIGADO POR LER
Objeto caro + reutilizável → não recrie a cada request.
Improper Instantiation é gastar CPU, memória, conexões e tempo recriando objetos que poderiam simplesmente ser reutilizados. Guarde o modelo mental: objeto caro, usado com frequência e thread-safe pede reutilização ou pooling — não um new a cada requisição.