C# - Implementações do Cache na plataforma .NET
![]() |
Hoje veremos as implementações do Cache na plataforma .NET |
Antes de entrar no assunto vejamos o conceito de Cache e Caching.
Cache refere-se à técnica de armazenar dados em um local de acesso rápido, de modo que esses dados possam ser recuperados rapidamente em uma solicitação subsequente, em vez de ter que ser recuperados novamente de um local mais lento. Caching, por sua vez, refere-se ao processo de armazenar esses dados em cache e gerenciar o acesso a eles.
O Cache é um tipo de memória relativamente pequena, mas que pode ser acessada muito rapidamente. Essencialmente, armazena informações que provavelmente serão usadas novamente. Por exemplo, os navegadores da Web normalmente usam um cache para fazer com que as páginas da Web sejam carregadas mais rapidamente armazenando uma cópia dos arquivos da página da Web localmente, como em seu computador local.
O Caching é o processo de armazenamento de dados em cache sendo que o armazenamento em cache com a linguagem C# é muito fácil pois o namespace System.Runtime.Caching.dll fornece os recursos para trabalhar com cache em C#.
É um conceito
simples, mas muito eficaz. A ideia é reaproveitar os resultados de uma operação.
Ao realizar uma operação pesada, salvaremos o resultado em nosso contêiner de
cache. Na próxima vez que precisarmos desse resultado, vamos obtê-lo do
contêiner de cache, em vez de executar a operação pesada novamente.
Por exemplo, para obter uma imagem de uma pessoa, podemos acessar um banco
de dados. Em vez de realizar essa viagem todas as vezes, podemos salvar a imagem
no cache, obtendo-o da memória sempre que precisarmos.
O armazenamento em cache funciona muito bem para dados que não são alterados com
frequência. Ou melhor ainda, dados que nunca mudam. Dados que mudam
constantemente, como a hora da máquina atual, não devem ser armazenados em cache
ou você obterá resultados errados.
Na plataforma .NET, o caching é usado para melhorar o desempenho de aplicativos, reduzindo o número de solicitações feitas a serviços externos ou recursos de armazenamento de dados, como bancos de dados. Em vez disso, o cache pode ser usado para armazenar dados que não mudam com frequência ou que podem ser pré-carregados para melhorar a resposta do aplicativo.
Existem 5 tipos de cache :
Vou focar nos 3 primeiros iniciando com o Cache persistente no processo apresentando uma implementação bem simples:
namespace Cache_Exemplos; public class CacheInProcess<TItem> public TItem GetOrCreate(object key, Func<TItem> createItem) |
Exemplo de uso do cache:
using Cache_Exemplos;
var
_imagemCache = new CacheInProcess<byte[]>(); |
Este código é bem
simples mas resolve um problema crucial. Para obter a imagem de um usuário,
apenas a primeira solicitação realizará uma viagem ao banco de dados. Os dados
da imagem (byte[]) são então salvos na memória do processo. Todas as
solicitações a seguir para a imagem serão extraídas da memória, economizando
tempo e recursos.
No entanto o código acima tem um problema, ele não é thread-safe e exceções
podem ocorrer quando usado em vários threads. Além disso, os itens armazenados
em cache ficarão na memória para sempre, o que na verdade é muito ruim.
Mas porque é tão ruim assim ficar com dados no Cache ?
Veremos a seguir as razões para isso:
Para lidar com esses problemas, as estruturas de cache têm políticas de despejo (também conhecidas como políticas de remoção que são regras para remover itens do cache de acordo com alguma lógica. As políticas comuns de despejo são:
Agora que sabemos o que precisamos, podemos procurar por soluções melhores.
Na plataforma .NET temos uma implementação para cache muito boa e veremos como usá-la com eficiência e como melhorá-la em alguns cenários. Na verdade temos duas soluções diferentes que usam os pacotes :
A recomendação é
usar o pacote Microsoft.Extensions.Caching.Memory
porque ele se integra melhor com a ASP. NET Core e pode ser facilmente injetado
no mecanismo de injeção de dependência.
Aqui está um exemplo básico com Microsoft.Extensions.Caching.Memory:
using Microsoft.Extensions.Caching.Memory; namespace Cache_Exemplos;
public class MemoryCacheSimples<TItem>
public TItem GetOrCreate(object key, Func<TItem>
createItem)
// Salva dados no cache |
Uso:
var _imagemCache = new
MemoryCacheSimples<byte[]>(); // ... var minhaImagem = _imagemCache.GetOrCreate(userId, () => _database.GetImagem(userId)); |
Dois destaques nesta implementação : Ela é uma implementação thread-safe e você pode chamar isso com segurança de vários threads ao mesmo tempo, e , a segunda coisa é que o MemoryCache permite todas as políticas de despejo.
A seguir temos um exemplo de código mostrando o uso destas políticas:
public class MemoryCacheComPolicitas<TItem> { private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions() { SizeLimit = 1024 }); public TItem GetOrCreate(object key, Func<TItem> createItem) { TItem cacheEntry; if (!_cache.TryGetValue(key, out cacheEntry)) // procura chave no cache { // chave não esta no cache, obtem dados cacheEntry = createItem(); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSize(1) //tamanho //Prioridade em remover quando alcancar o tamanho limite .SetPriority(CacheItemPriority.High) // Mantem no cache, reseta o tempo se acessado .SetSlidingExpiration(TimeSpan.FromSeconds(2)) // Remove do cache depois deste tempo, .SetAbsoluteExpiration(TimeSpan.FromSeconds(10)); // Salva dados no cache. |
Vamos entender o código:
1 - O tamanho limite foi adicionado em MemoryCacheOptions e isso adiciona uma política baseada em tamanho ao nosso contêiner de cache. O tamanho não tem uma unidade. Em vez disso, precisamos definir a quantidade de tamanho em cada entrada de cache. Nesse caso, definimos o valor como 1 a cada vez com SetSize(1). Isso significa que o cache está limitado a 1024 itens.
2- Quando atingirmos o limite de tamanho, qual item de cache deve ser removido? Você pode realmente definir a prioridade com .SetPriority(CacheItemPriority.High). Os níveis são Baixo, Normal, Alto e NeverRemove.
3- Incluímos SetSlidingExpiration(TimeSpan.FromSeconds(2)) e isto define a expiração deslizante para 2 segundos. Isso significa que se um item não for acessado em mais de 2 segundos, ele será removido.
4- foi adicionado SetAbsoluteExpiration(TimeSpan.FromSeconds(10)) que define a expiração absoluta para 10 segundos. Isso significa que o item será despejado em 10 segundos, se ainda não o foi.
Além das opções no
exemplo, você também pode definir um delegate
RegisterPostEvictionCallback, que será chamado quando um item for
despejado e onde o retorno de chamada fornecido será acionado depois que a
entrada de cache for removida do cache.
Esse é um conjunto de recursos bastante abrangente e faz você se perguntar
se há mais alguma coisa a acrescentar ou podemos parar por aqui.
Sempre há
algo a acrescentar ....
Mas isso é assunto para outro artigo.
"O Senhor
estabeleceu o seu trono nos céus, e como rei domina sobre tudo o que
existe."
Salmos 103:19
Referências:
ASP .NET - Gerando QRCode com a API do Google
C# 9.0 - Instruções de nível superior
ASP.NET Core Web API - Apresentando API Analyzers
ASP.NET Core - Usando o token JWT com o Swagger
Gerando a documentação da Web API com Swagger