implementar cache de memória em uma aplicação ASP.NET Core. Entretanto, este tipo de cache não fornece bons resultados quando há mais de uma instância da aplicação, por exemplo, quando é publicada em PaaS. Para este cenário pode ser utilizado cache distribuído.
Cache distribuído é um cache compartilhado entre as instâncias de uma aplicação. Tipicamente gerenciado por um serviço externo que a aplicação acessa. Este tipo de cache pode melhorar a performance da aplicação, além de facilitar a sua escalabilidade.
Quando se trabalha com cache distribuído, o dado:
No ASP.NET Core o cache distribuído pode ser implementado, utilizando SQL Server, Redis ou serviços de terceiro, como o NCache. Como o NCache é uma biblioteca paga, não irei abordá-la neste artigo. No caso das outras duas formas, começaremos nosso estudo pelo Redis.
O Redis é um banco de dados estruturados em memória, open source, que pode ser utilizado como database, cache e/ou mensageria. O seu uso mais comum é como sistema de cache e é como o utilizaremos neste artigo.
Para que seja utilizado é necessário que o Redis esteja instalado na máquina atual ou em algum servidor. O seu instalador pode ser obtido no site dele. Entretanto, neste artigo farei uso a versão “containeralizada”, via Docker.
Com isso, caso tenha o Docker instalado na sua máquina, o Redis pode ser inicializado com o comando abaixo:
docker run --name local-redis -p 6379:6379 -d redis
Como Docker não é o foco deste artigo, não expliquei o comando acima, nele o importante é a porta definida no host local (6379
), pois ela será informada no ASP.NET Core.
Assim como o cache de memória, o cache distribuído é implementado via uma interface, a IDistributedCache
. Entretanto o ASP.NET não fornece uma implementação padrão para esta interface, cada uma das implementações de cache distribuído são fornecidas via pacotes NuGet.
No caso do Redis, é necessário adicionar na aplicação o pacote Microsoft.Extensions.Caching.StackExchangeRedis
:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
Em seguida é necessário registrá-lo no método ConfigureServices
da classe Startup
:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
}
Com isso, a implementação da interface IDistributedCache
será “injetada” das dependências da aplicação e poderá ser acessada no construtor:
public class ProductsController : Controller
{
private readonly IDistributedCache _cache;
public ProductsController(IDistributedCache cache)
{
_cache = cache;
}
}
Para ter acesso a um valor do cache podemos utilizar os métodos Get
, GetAsync
, que retorna o valor como um array de bytes; ou GetString
, GetStringAsync
, que o retorna como uma string:
public async Task<IActionResult> Index()
{
var cacheKey = "Products";
var products = new List<Product>();
var json = await _cache.GetStringAsync(cacheKey);
if(json != null)
{
products = JsonSerializer.Deserialize<List<Product>>(json);
}
return View(products);
}
Já para salvar um valor no cache, também temos quatro métodos: Set
e SetAsync
, que salva o valor como um array de bytes; e SetString
e SetStringAsync
que o salva como uma string:
public async Task<IActionResult> Index()
{
var cacheKey = "Products";
var products = new List<Product>();
var json = await _cache.GetStringAsync(cacheKey);
if(json != null)
{
products = JsonSerializer.Deserialize<List<Product>>(json);
}
else {
products = await _context.Products.ToListAsync();
json = JsonSerializer.Serialize<List<Product>>(products);
await _cache.SetStringAsync(cacheKey, json);
}
return View(products);
}
Caso queira, também é possível remover o item do cache com os métodos Remove
e RemoveAsync
.
Pela nossa configuração atual, podemos correr o risco de ter dados obsoletos no cache, já que não está sendo definido nenhum prazo de expiração dos dados salvos nele. Podemos fazer isso utilizando a classe DistributedCacheEntryOptions
:
products = await _context.Products.ToListAsync();
json = JsonSerializer.Serialize<List<Product>>(products);
var options = new DistributedCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(20))
.SetAbsoluteExpiration(TimeSpan.FromMinutes(1));
await _cache.SetStringAsync(cacheKey, json);
Agora o cache poderá ficar inativo até 20 segundos e será removido depois de um minuto. O tempo do “SlidingExpiration” também pode ser reiniciado com os métodos Refresh
e RefreshAsync
.
Caso tenha intenção de utilizar o SQL Server no lugar do Redis como cache distribuído, inicialmente é necessário executar o comando abaixo:
dotnet sql-cache create <conexao-sql-server> <schema> <tabela>
Para criar no banco a tabela que será utilizada para armazenar os dados do cache.
Em seguida, adicione no projeto o pacote Microsoft.Extensions.Caching.SqlServer
:
dotnet add package Microsoft.Extensions.Caching.SqlServer
E por fim, configure o cache no método ConfigureServices
da classe Startup
:
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "<conexao-sql-server>";
options.SchemaName = <schema>;
options.TableName = <tabela>;
});
}
Note que nesta configuração devem ser informados os mesmos dados passados no comando dotnet sql-cache create
.
O uso deste tipo de cache não difere do que vimos com o Redis.
Como o cache de memória deve ser evitado em uma aplicação “multi-servidor”, se este for o seu caso, não hesite em implementar o cache distribuído. Se a sua equipe estiver mais familiarizada com o SQL Server, mesmo que a sua performance seja inferior ao Redis, esta é uma boa escolha. Se este não for o caso, procure sempre optar por uma solução “em-memória”, como o Redis e o NCache.
Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.
Todos os artigosA performance de um site pode ser um favor determinante para o seu sucesso ou fracasso, veja como me...
Verificar e monitorar a integridade de uma aplicação web é algo essencial, principalmente em ambient...
Continuando com nossa série de artigos sobre os termos comuns de segurança, vamos abordar hoje o DDo...
Veja neste artigo os principais tipos de banco de dados NoSQL.
Conheça o framework Microsoft Orleans e veja como ele tenta resolver o "problema" dos sistemas distr...
Vamos conhecer nesse artigo o que é um CDN, como ele funciona, quais suas vantagens e os principais...
Veja todo o processo de publicação de deploy de uma aplicação ASP.NET Core no Linux utilizando o Ngi...
Implemente testes unitários em requisições HTTP com o auxílio de Flurl.
Veja como consumir dados de uma API no C#, utilizando a biblioteca Flurl.
Conheça os novos tipos de dados introduzidos no C# 9.0, os inteiros nativos e os novos recursos da i...
Dando continuidade ao nosso estudo do C# 9.0, neste artigo veremos dois novos recursos dele: Program...
Já estamos no C# 9.0, a nova versão lançada no .NET Conf 2020 trouxe uma série de novos recursos, ve...