No universo do desenvolvimento de aplicações com ASP.NET Core, a busca por performance e escalabilidade é constante. Uma das estratégias mais eficazes para atingir esses objetivos é a implementação de caching. O caching, em sua essência, consiste em armazenar dados temporariamente para que possam ser acessados mais rapidamente em requisições futuras. Em ASP.NET Core, temos diversas opções de caching, desde as mais simples, como o in-memory caching, até as mais sofisticadas, como o distributed caching e o hybrid caching.
Tipos de Caching em ASP.NET Core
O ASP.NET Core oferece flexibilidade para o desenvolvedor escolher onde e como o cache será aplicado. Podemos optar por:
- Response Caching: Armazena respostas do servidor no cliente, utilizando headers HTTP para controlar o comportamento do cache.
- Output Caching: Permite um controle mais granular sobre o cache de respostas do servidor, configurado no próprio servidor.
- In-Memory Caching: Utiliza a memória do servidor para armazenar os dados em cache. É uma opção rápida, mas limitada à capacidade de um único servidor.
- Distributed Caching: Distribui o cache entre vários servidores, permitindo escalar a aplicação e aumentar a disponibilidade. Tecnologias como Redis e SQL Server podem ser utilizadas para implementar o distributed caching.
- Hybrid Caching: Combina as vantagens do in-memory caching e do distributed caching, oferecendo alta performance e durabilidade.
Implementando Caching em Minimal APIs
As Minimal APIs do ASP.NET Core oferecem uma forma simplificada de criar endpoints HTTP. Implementar caching nessas APIs é relativamente simples e pode trazer ganhos significativos de performance.
In-Memory Caching
Para utilizar o in-memory caching, podemos injetar a interface IMemoryCache
no endpoint da API. O código abaixo demonstra como buscar dados no cache e, caso não estejam presentes, buscar no repositório, armazenar no cache e retornar:
app.MapGet("/authors/getall", (IMemoryCache cache, IAuthorRepository authorRepository) =>
{
if (!cache.TryGetValue("get-authors", out List authors))
{
authors = authorRepository.GetAll();
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5))
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
cache.Set("get-authors", authors, cacheEntryOptions);
}
return Results.Ok(authors);
});
Nesse exemplo, definimos um tempo de expiração absoluto de 5 minutos e um tempo de expiração deslizante de 1 minuto. Isso significa que o cache será invalidado após 5 minutos, ou após 1 minuto de inatividade.
Distributed Caching
O distributed caching é ideal para aplicações que precisam escalar horizontalmente. Para utilizá-lo, injetamos a interface IDistributedCache
no endpoint da API. O exemplo a seguir mostra como implementar o distributed caching:
app.MapGet("/getallauthors", async (IDistributedCache cache) =>
{
var cacheKey = "get-all-authors";
var cachedMessage = await cache.GetStringAsync(cacheKey);
if (cachedMessage == null)
{
cachedMessage = $"The data has been cached at {DateTime.Now}";
await cache.SetStringAsync(cacheKey, cachedMessage, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60)
});
}
return Results.Ok(cachedMessage);
});
Neste caso, o cache expira após 60 segundos. É importante configurar corretamente a conexão com o servidor de cache distribuído, como Redis ou SQL Server, na inicialização da aplicação.
Hybrid Caching
A partir do .NET 9, o Hybrid Caching oferece uma abordagem que combina as vantagens do in-memory caching e do distributed caching. Ele pode ser configurado da seguinte forma:
services.AddHybridCache(options => {
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
Essa configuração define um tempo de expiração padrão de 5 minutos tanto para o cache local (in-memory) quanto para o cache distribuído.
Response Caching
O Response Caching utiliza os headers HTTP para controlar o cache no cliente. Para habilitá-lo, adicionamos o middleware de Response Caching à pipeline da aplicação:
builder.Services.AddResponseCaching();
app.UseResponseCaching();
O cliente, ao receber a resposta, poderá armazená-la em cache de acordo com os headers definidos.
Output Caching
O Output Caching permite um controle mais preciso sobre o cache no servidor. Podemos implementar o Output Caching utilizando o método CacheOutput
:
app.MapPost("/author/getauthors", ([FromServices] IAuthorRepository authorRepository) =>
{
return authorRepository.GetAll();
}).CacheOutput(x => x.Expire(TimeSpan.FromSeconds(30)));
Neste exemplo, a resposta do endpoint será armazenada em cache por 30 segundos. Ao contrário do Response Caching, o Output Caching pode ser utilizado com in-memory, distributed ou hybrid caching.
Melhores Práticas de Caching
Para obter o máximo proveito do caching em suas aplicações ASP.NET Core, siga estas melhores práticas:
- Escolha a estratégia de caching correta: Utilize in-memory caching para aplicações com menor volume de dados e distributed caching para aplicações que precisam escalar.
- Defina políticas de expiração adequadas: Ajuste os tempos de expiração de acordo com a volatilidade dos dados.
- Não armazene dados sensíveis em cache: Proteja informações confidenciais evitando armazená-las em cache.
- Utilize a invalidação de cache quando necessário: Remova entradas de cache quando os dados forem alterados.
- Monitore a taxa de acerto e erro do cache: Acompanhe as métricas do cache para entender seu desempenho e ajustar as configurações.
Conclusão
O caching é uma ferramenta poderosa para otimizar aplicações ASP.NET Core. Ao escolher a estratégia de caching correta e seguir as melhores práticas, é possível reduzir a latência, aumentar a escalabilidade e melhorar a experiência do usuário. As Minimal APIs simplificam a implementação de caching, tornando-o acessível mesmo em projetos menores. A crescente adoção e a constante evolução das opções de caching no .NET, como a inclusão do Hybrid Cache, reforçam a importância desta técnica no desenvolvimento moderno de aplicações web. Ao implementar caching, os desenvolvedores precisam equilibrar a necessidade de dados atualizados com os benefícios de performance, ajustando os tempos de expiração e utilizando a invalidação de cache de forma inteligente. Não se trata apenas de uma otimização técnica, mas de uma consideração estratégica que pode impactar diretamente a satisfação do usuário e a eficiência da aplicação.