Home/Insights/Leitura

Como escalar APIs .NET para alta demanda

Quando a API vira ponto crítico da operação, performance não depende só de infraestrutura. O desenho de contrato, o tratamento de fila, a resiliência e a observabilidade pesam tanto quanto CPU e memória.

Os gargalos mais comuns

APIs tendem a sofrer com payload excessivo, consulta ineficiente, falta de cache, integração síncrona demais, retry mal desenhado e pouca visibilidade sobre o que realmente está estourando latência ou erro.

Pilares para escalar com previsibilidade

  • Contratos claros e versionados
  • Observabilidade em latência, erro e throughput
  • Filas e processamento assíncrono onde faz sentido
  • Cache e otimização de leitura sem quebrar consistência
  • Idempotência e controle de retry

Modernizando sistemas .NET?

Veja como a ArrexTec ajuda empresas nessa situação — sem reescrever tudo do zero.

Ver serviço de APIs

Quando a arquitetura precisa mudar

Se a escala já está amarrada a domínios com ritmos diferentes, integrações sensíveis e responsabilidade confusa, talvez o próximo passo não seja só tuning. Pode ser redesenho arquitetural com APIs, eventos e serviços melhor distribuídos.

Medir antes de otimizar: esqueça a média

A primeira regra de escalabilidade é não adivinhar. E a segunda é não olhar a latência média — ela esconde exatamente o problema que você precisa achar.

Uma API com média de 120 ms pode ter p99 de 8 segundos. Significa que 1% das requisições demora oito segundos, e esse 1% costuma ser o cliente maior, com mais dados, na hora de pico. A média diz que está tudo bem; o p99 diz onde o negócio está sendo prejudicado.

Acompanhe sempre p50, p95 e p99 separados, com throughput e taxa de erro. No .NET, o OpenTelemetry já expõe isso sem instrumentação manual, e o dotnet-counters resolve diagnóstico pontual em produção sem precisar de ferramenta externa.

Sem esses números, otimização vira palpite — e o palpite costuma cair no lugar errado.

O gargalo quase sempre está no banco

Antes de pensar em mais instâncias, vale olhar o acesso a dados. É onde está a maior parte do tempo perdido em API corporativa .NET:

  • N+1 — a consulta traz 200 pedidos e, ao percorrer a lista, dispara uma query por item. Duzentas idas ao banco onde deveria haver uma. É o problema mais comum e o mais fácil de corrigir.
  • Materializar o que não usa — carregar a entidade completa para exibir três colunas. Projetar direto para DTO com Select reduz tráfego e alocação de memória.
  • Tracking desnecessário — o EF Core rastreia mudanças por padrão. Em consulta somente leitura, AsNoTracking() elimina esse trabalho.
  • Falta de índice — a consulta que era rápida com dez mil registros deixa de ser com dois milhões. O plano de execução mostra isso em segundos.
  • Paginação inexistente — endpoint que devolve a tabela inteira funciona até o dia em que não funciona mais, e falha exatamente quando o cliente cresceu.

Corrigir esses cinco pontos costuma render mais que qualquer mudança de infraestrutura, e custa incomparavelmente menos.

O erro de concorrência que derruba a API inteira

Existe uma falha específica em .NET que não aparece em teste e destrói a aplicação sob carga: bloquear thread esperando operação assíncrona. É o famoso .Result ou .Wait() em cima de um método async.

Cada chamada dessas prende uma thread do pool sem fazer nada. Com pouca carga, ninguém percebe. Sob carga, o pool esgota, novas requisições ficam na fila esperando thread livre, a latência dispara e a API para de responder — sem CPU alta, sem erro no log, sem causa aparente.

A correção é async de ponta a ponta: se o método chama algo assíncrono, ele é async e usa await. Sem exceção no caminho da requisição. É a mudança de maior impacto por linha alterada em APIs .NET com problema de escala.

Cache: onde ele ajuda e onde ele engana

Cache é a ferramenta mais eficaz e a mais mal usada. Três níveis, com custos diferentes:

  • Em memória — o mais rápido, mas cada instância tem o seu. Com várias réplicas, o mesmo dado fica em versões diferentes.
  • Distribuído — Redis e similares. Consistente entre instâncias, com custo de rede a cada acesso.
  • HTTP — ETag e Cache-Control. O mais barato de todos, porque a requisição nem chega na sua aplicação.

A parte difícil nunca é guardar, é invalidar. Cache sem estratégia de expiração clara transforma bug de dado desatualizado em algo intermitente e quase impossível de reproduzir — e isso é pior que a lentidão que ele resolveu.

Regra de segurança: só coloque em cache o que pode ficar desatualizado por um período que o negócio aceite explicitamente. Se ninguém souber responder qual é esse período, não é candidato a cache.

Proteger a API de si mesma

Escalar não é só aguentar mais carga; é degradar de forma controlada quando a carga passa do limite. Quatro mecanismos, todos disponíveis no .NET moderno sem dependência externa relevante:

  • Rate limiting — nativo desde o .NET 7. Impede que um cliente mal comportado consuma a capacidade de todos.
  • Timeout em toda chamada externa — sem timeout, uma dependência lenta vira uma fila crescente de requisições penduradas.
  • Circuit breaker — quando a dependência está fora, falhe rápido em vez de acumular espera. Polly resolve isso com poucas linhas.
  • Idempotência — se o cliente faz retry, a operação não pode ser aplicada duas vezes. Chave de idempotência no comando é o que evita o pedido duplicado que aparece depois no financeiro.

Sem esses quatro, retry automático deixa de ser resiliência e passa a ser amplificador de falha: a API já sobrecarregada recebe o triplo de requisições.

Perguntas frequentes

Como saber se o problema é a API ou o banco?

Instrumente as duas camadas separadamente. Se a latência da aplicação sobe junto com o tempo de query, o gargalo é dados. Se a query está rápida mas a requisição demora, olhe esgotamento do pool de threads ou de conexões — os dois sintomas se parecem e têm causas opostas.

Vale mais escalar horizontalmente ou otimizar o código?

Otimize primeiro, quase sempre. Adicionar instâncias multiplica o custo e costuma piorar o gargalo quando ele está no banco — mais réplicas significam mais conexões concorrendo pelo mesmo recurso. Escalonamento horizontal resolve limite de CPU e memória, não consulta ineficiente.

Async/await melhora a performance?

Não melhora a latência de uma requisição isolada. Melhora a capacidade de atender requisições simultâneas, porque libera a thread durante a espera de I/O. Em API sob carga, isso é a diferença entre atender e enfileirar.

Quando usar fila em vez de resposta síncrona?

Quando o cliente não precisa do resultado imediatamente e a operação é demorada ou depende de terceiros instáveis. Processamento de arquivo, envio de notificação e integração com sistema externo lento são candidatos naturais. A API responde aceitando o pedido e o trabalho acontece fora do ciclo da requisição.

Preciso migrar para microsserviços para escalar?

Não. A maioria dos problemas de escala em API corporativa se resolve com consulta eficiente, cache e async correto — sem tocar na arquitetura. Microsserviços fazem sentido quando partes do sistema precisam escalar de forma independente, e o tema é tratado em arquitetura de microsserviços com .NET 8.

continuar explorando

Serviços conectados a esse tema.

Pronto para um diagnóstico técnico gratuito?

Conte o cenário e entendemos juntos qual é o próximo passo.