À medida que aplicações web crescem em complexidade e demanda, torna-se cada vez mais importante pensar na escalabilidade. No ecossistema .NET, uma das ferramentas mais poderosas e flexíveis para ajudar nesse processo é o YARP (Yet Another Reverse Proxy).
Neste artigo, vamos explorar como escalar aplicações com o YARP, com exemplos práticos e explicações detalhadas.
O que é o YARP?
YARP é um projeto open source da Microsoft que fornece um proxy reverso altamente customizável para aplicações .NET. Ele é construído sobre o ASP.NET Core e utiliza todos os benefícios da plataforma .NET moderno, incluindo middleware, injeção de dependência e configuração baseada em código ou arquivos.
Com o YARP, é possível:
- Distribuir requisições entre múltiplas instâncias de uma aplicação (load balancing).
- Criar gateways de API.
- Encaminhar requisições com base em headers, caminhos ou outros critérios.
- Implementar estratégias de failover e segurança.
- E o foco deste post: escalar aplicações horizontalmente.
Vamos considerar um cenário onde você possui uma aplicação web composta por diferentes microsserviços:
api.produtos
api.usuarios
api.pedidos
Ao invés de expor cada serviço em uma porta diferente ou configurar um API Gateway externo, podemos usar o YARP para orquestrar tudo isso dentro do ecossistema .NET.
Instalando o YARP
Crie um novo projeto .NET 9.0 (Web Application) e adicione o pacote do YARP:
dotnet add package Yarp.ReverseProxy
Configure o arquivo appsettings.json
com o exemplo de múltiplos destino com o código abaixo:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"produtos": {
"ClusterId": "cluster-produtos",
"Match": {
"Path": "/produtos/{**catch-all}"
}
},
"usuarios": {
"ClusterId": "cluster-usuarios",
"Match": {
"Path": "/usuarios/{**catch-all}"
}
},
"pedidos": {
"ClusterId": "cluster-pedidos",
"Match": {
"Path": "/pedidos/{**catch-all}"
}
}
},
"Clusters": {
"cluster-produtos": {
"Destinations": {
"destino1": {
"Address": "http://localhost:5001/"
},
"destino2": {
"Address": "http://localhost:5002/"
}
}
},
"cluster-usuarios": {
"Destinations": {
"destino1": {
"Address": "http://localhost:6001/"
}
}
},
"cluster-pedidos": {
"Destinations": {
"destino1": {
"Address": "http://localhost:7001/"
},
"destino2": {
"Address": "http://localhost:7002/"
},
"destino3": {
"Address": "http://localhost:7003/"
}
}
}
}
}
}
Neste exemplo, os serviços produtos
e pedidos
estão com múltiplas instâncias (ótimo para escalabilidade horizontal).
Configurando o YARP
Na classe Program.cs, configure com o código abaixo:
try
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers();
// Adiciona suporte ao Reverse Proxy
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddOpenTelemetry();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllers();
app.MapReverseProxy();
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.Information("Server Shutting down...");
Log.CloseAndFlush();
}
Load Balancing e Estratégias
Por padrão, o YARP já faz round-robin entre os destinos configurados, mas você pode personalizar o comportamento com políticas:
"cluster-pedidos": {
"LoadBalancingPolicy": "LeastRequests",
"Destinations": {
"destino1": { "Address": "http://localhost:7001/" },
"destino2": { "Address": "http://localhost:7002/" },
"destino3": { "Address": "http://localhost:7003/" }
}
}
Políticas suportadas incluem:
FirstAlphabetical
Random
RoundRobin
(default)PowerOfTwoChoices
LeastRequests
As políticas detalhadas de cada um você encontra neste link.
Escalando com Kubernetes ou Docker
Em produção, os destinos não seriam portas locais, mas sim containers com réplicas gerenciadas pelo Kubernetes ou Docker Swarm. Você pode configurar o endereço como um DNS interno de um serviço no cluster:
"Address": "http://api-pedidos-service/"
Com isso, o YARP se conecta às instâncias expostas pelo serviço, automaticamente distribuindo as requisições conforme as réplicas disponíveis.
Cluster de Pedidos com 3 Instâncias
Vamos imaginar que temos três instâncias do serviço de pedidos rodando em:
http://pedidos-instance1.internal
http://pedidos-instance2.internal
http://pedidos-instance3.internal
Configuração YARP:
"cluster-pedidos": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"i1": { "Address": "http://pedidos-instance1.internal/" },
"i2": { "Address": "http://pedidos-instance2.internal/" },
"i3": { "Address": "http://pedidos-instance3.internal/" }
}
}
Agora, todas as requisições que chegam em /pedidos
serão distribuídas de forma balanceada entre as três instâncias.
Monitoramento e Observabilidade
Por ser uma aplicação ASP.NET Core, você pode adicionar o middleware de logging, tracing com OpenTelemetry e usar ferramentas como Application Insights ou Prometheus/Grafana para acompanhar as métricas do seu proxy.
No Program.cs
, você pode adicionar o seguinte setup:
var builder = WebApplication.CreateBuilder(args);
// Logging com Serilog (exemplo)
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration
.ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console();
});
// Telemetria com OpenTelemetry
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(); // Exporta para Jaeger, Zipkin ou outros
})
.WithMetrics(metrics =>
{
metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation()
.AddProcessInstrumentation()
.AddPrometheusExporter();
});
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.UseSerilogRequestLogging(); // Middleware de logging
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxy();
endpoints.MapPrometheusScrapingEndpoint(); // Exposição das métricas
});
app.Run();
Com isso:
- As requisições roteadas pelo YARP serão monitoradas.
- Métricas de latência, volume e status codes estarão disponíveis via Prometheus.
- Traces distribuídos podem ser visualizados no Jaeger ou Zipkin.
- Logs estruturados vão facilitar a análise de comportamento e erros.
Vantagens de Usar o YARP
- Totalmente integrado ao ecossistema .NET.
- Fácil de configurar.
- Ideal para microsserviços e cenários de escalabilidade.
- Permite roteamento inteligente baseado em headers, path, claims, etc.
- Open Source e com suporte ativo da Microsoft.
Conclusão
O YARP é uma solução robusta, flexível e poderosa para escalar aplicações .NET. Seja para balancear carga, consolidar endpoints de microsserviços ou montar um gateway de API, ele oferece tudo o que você precisa com a simplicidade do ASP.NET Core.
Se você ainda está usando proxies reversos externos ou soluções complexas para algo que poderia estar sob seu controle no código, vale a pena dar uma chance ao YARP.
Os detalhes completo desta série você encontra no meu GitHub: https://github.com/hgmauri/sample-yarp