Consumindo Memcached com .Net core

Consumindo Memcached com .Net core

Veja nesse artigo como adicionar cache na sua aplicação .NET

Introdução

Provavelmente em algum momento da sua vida alguém já pediu para você limpar o cache de alguma aplicação, seja em um web site de noticias ou em um aplicativo mobile.

No mundo do desenvolvimento de software esta estratégia de cache é muito comum, ela nos ajuda a garantir a performance das nossas aplicações fazendo com que pesquisas fiquem mais rápidas ou que as páginas do nosso site carreguem mais rápido.

Mas como isso funciona?

A seguir você tem um desenho demonstrando uma aplicação simples sem cache:

Imagine que hoje o meusite.com.br tem 5000 mil acessos por dia e em cada um dos acessos ele vai até o banco de dados buscar a informação. Até aqui tranquilo né? A infra do servidor esta aguentando e não temos nenhum gargalo até este momento.

Agora imagine que aconteceu algo muito importante e o meusite.com.br é o primeiro a liberar a noticia sobre este fato importante e de 5000 mil acessos diários o site passa a receber 5000 por minuto, o que vai acontecer com o nosso sistema? Provavelmente ele vai cair correto?

Obs.: Neste momento não estou falando de estratégia de escalar o sistema...etc, estou focando em uma arquitetura simples onde nós temos um site -> uma api e um banco de dados.

Para resolver este problema nós podemos utilizar uma estratégia de cache. A seguir você o desenho anterior atualizado com uma camada de cache:

Existem muitas estratégias de cache, neste post rápido demonstrarei como adicionar uma camada de cache na sua aplicação .NET utilizando o Memcached.

Caso você não conheça o Memcached, ele tem como objetivo oferecer um sistema de cache de memória distribuída. Ele é frequentemente usado para acelerar sites dinâmicos através do cache de dados e objetos na memória RAM. É muito eficiente para reduzir o número de vezes que uma fonte de dados externa (como um banco de dados ou API, por exemplo) deve ser lida. Caso tenha interesse em saber mais sobre ele, eu sugiro uma rápida leitura no seguinte artigo: Memcached Docs.

Bom, para os próximos passos, será necessário ter um servidor com o Memcached instalado. Para isso, eu utilizarei o docker. Caso esse seja o seu primeiro contato com ele, o Docker é um projeto open source escrito na linguagem Go, que torna a criação e o gerenciamento de contêineres muito mais fácil. Um contêiner (container) é construído utilizando alguns recursos do kernel, como namespaces, cgroups, chroot, que permitem que ele seja criado e rode como um processo isolado dentro do nosso Sistema Operacional.

Antes dele a gente precisava instalar diversas ferramentas na nossas estações de trabalho, hoje como demonstrarei para vocês neste artigo, nós podemos criar um container com uma tecnologia X ou Y sem a necessidade de alocar muitos recursos no nosso computador.

Para prática

Bom, agora que nós sabemos o que seria uma estratégia de cache e o Docker, crie um novo arquivo chamado docker-compose.yml em um diretório dentro do seu computador, em seguida atualize ele com o seguinte trecho de código:

version: "3"
services: 
    memcached:
        image: memcached:alpine
        ports: 
            - 11211:11211
Memcached no Docker

Em seguida, execute o comando docker-compose up para que o seu container com o memcached seja criado.

Caso tudo esteja funcionando corretamente, você deve receber o seguinte resultado na sua console:

Com a parte do memcached OK, vamos para nossa aplicação. Crie um novo projeto com o template .NET API e, em seguida, adicione a biblioteca EnyimMemcachedCore nele:

EnyimMemcachedCore biblioteca .NET para Memcached

Agora, crie um novo diretório na sua aplicação chamado Service com três arquivos dentro: ICacheService.cs, MemoryCacheService.cs e  CepService.cs e, em seguida, atualize com os trechos de código abaixo:

ICacheService.cs

    public interface ICacheService
    {
        object Get(string key);

        void Set(string key, object content);

        void Remove(string key);
    }

O ICacheService é um interface com três métodos: Get que recebe uma Key para buscar um valor através de uma chave no cache, um Set para que possamos salvar algum objeto no cache e o Remove para que possamos deletar algum valor dentro do nosso cache.

MemoryCacheService.cs

public class MemoryCacheService : ICacheService
    {

        private readonly IMemcachedClient _cache;
        private const int cacheSeconds = 600;

        public MemoryCacheService(IMemcachedClient cache)
        {
            _cache = cache; 
        }

        public object Get(string key)
        {
            return _cache.Get(key);
        }

        public void Remove(string key)
        {
            _cache.Remove(key);
        }

        public void Set(string key, object content)
        {
            _cache.Add(key, content, cacheSeconds);
        }
    }

Em MemoryCacheService,  nós temos a implementação da interface ICacheService.

CepService

 public class CepService
    {
        private ICacheService _service;
        private static readonly HttpClient HttpClient = new HttpClient();

        public CepService(ICacheService service)
        {
            _service = service;
        }

        public async Task<dynamic> BuscarCep(string cep)
        {
            object result = _service.Get($"BuscarCep-{cep}");

            if (result != null)
                return result;

            var url = $"https://viacep.com.br/ws/{cep}/json/";

            var resultado = await HttpClient.GetAsync(url);
            if (resultado.StatusCode != System.Net.HttpStatusCode.OK)
                throw new HttpRequestException($"{resultado.StatusCode}-{resultado.RequestMessage}");

            result = await resultado.Content.ReadAsStringAsync();
            _service.Set($"BuscarCep-{cep}", result);

            return result;
        }
    }

Em CepService, nos temos um serviço básico que faz uma pesquisa por CEP na API do viacep e salva no Cache. Nas próximas requisições, ele verifica se o valor já existe no cache, caso sim, ele retorna de lá não sendo mais necessário a pesquisa na API.

Com os Services criados, o próximo passo será registrá-los no DI do .NET. Para isso, abra o arquivo Program.cs e adicione nele os seguintes trechos de código:

//...outras implementações

builder.Services.AddSwaggerGen();

#region [Memcached]
/* Configuração do Memcached */
builder.Services.AddEnyimMemcached(memcachedClientOptions =>
{
    memcachedClientOptions.Servers.Add(new Server
    {
        Address = "127.0.0.1",
        Port = 11211
    });
});
#endregion


#region [DI]
/* Registrando os services */
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();
builder.Services.AddTransient<CepService>();
#endregion

//...outras implementações
app.UseAuthorization();

#region [Memcached]
/* adicionando o memcached na pipe */
app.UseEnyimMemcached();
#endregion

Para testar este fluxo, crie uma nova Controller chamada CepController e adicione o seguinte trecho de código:

 public class CepController : ControllerBase
    {


        private readonly ILogger<CepController> _logger;
        private readonly CepService _service;

        public CepController(ILogger<CepController> logger, CepService service)
        {
            _logger = logger;
            _service = service;
        }

        [HttpGet]
        public async Task<IActionResult> Get(string cep)
        {
            return Ok(await _service.BuscarCep(cep));
        }
    }

Testando o fluxo

Para validar este fluxo eu gravei um vídeo, segue link abaixo:

.NET + Memcached

Link da versão final desenvolvida neste artigo: .NET Memcached

Bom, é isso pessoal, espero que tenham gostado e até um próximo artigo :)