Ir para o conteúdo principal

Portal do Cidadão - Estudo de Melhoria de Performance

OBJETIVO

Mapear soluções para melhoria de performance no projeto Portal do Cidadão conforme a User Story: “Eu como vingadores, preciso realizar um estudo de caso para melhoria de perfomance no Portal do Cidadão" presente na Sprint número 100 do time Vingadores.


JUSTIFICATIVA

Tendo em vista que o Portal do Cidadão terá um alto volume de acessos, os usuários poderão experimentar lentidões ou instabilidades durante o seu uso. Desta forma faz-se necessário a elaboração deste estudo para mapear possíveis soluções e ferramentas para a garantia da disponibilidade do sistema.


RESULTADOS ESPERADOS

Levantar ferramentas, técnicas ou soluções disponíveis que possam impactar significativamente na melhoria de performance do sistema parcialmente ou como um todo.


ENVOLVIDOS:

Gustavo Felix Gomes (Dev team)

Rafael Passos dos Santos (Dev team)

Emanuel Rufino Alcantara de Lima (Dev team)

Lucas de Souza e Sousa (Dev team)

André Honório de Andrade Silva (Dev team)

Alef Carvalho (Dev team)

Euriane Nogueira Frota (Product Owner)


INTRODUÇÃO

É comum termos diversos dados das nossas aplicações que não mudam com frequência. Tudo que tem baixa mutabilidade, pode ser cacheado, evitando que a requisição precise chegar até a fonte de dados, sobrecarregando-a sem necessidade.

A técnica de cache normalmente é implementada entra a aplicação e a sua fonte de dados. Quando uma requisição ocorre, podemos cachear seus resultados em algum local e posteriormente, toda vez que esta mesma requisição for chamada, lemos estes dados ao invés de consultar o banco de dados.

O cache pode ser salvo em diversos lugares, incluindo memória e disco. Todos os dados possuem um tempo de expiração que quando atingido força a renovação do mesmo. Essa renovação significa que a próxima requisição irá novamente até o banco de dados e o processo de caching será feito novamente.


CENÁRIO ATUAL

No Portal do Cidadão, não há nenhum tipo de caching de dados ou estratégia semelhante em execução. Devido ao alto volume de acessos previsto após o lançamento da plataforma faz-se necessário a implementação de uma ou mais técnicas deste tipo afim de garantir a performance da aplicação em produção.


MEMCACHE

Memcached é um sistema de cache de objetos de memória distribuída de código aberto, de alto desempenho, que ajuda na redução da carga do banco de dados. Ele mantém os dados como uma loja de valor de chave na memória para pequenos pedaços de dados arbitrários (strings, objetos) que podem ser resultado de chamadas de API, leituras de banco de dados e assim por diante.


REDIS

Redis é um banco de dados estruturados em memória, open source, que pode ser utilizado como database, cache e/ou mensageria porém o seu uso mais comum é como sistema de cache.


Exemplo:IMPLEMENTAÇÃO DO REDIS:

1 - Adicionar o pacote Nuget para Redis.

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis

2 - Em seguida registrá-lo no método ConfigureServices da classe Startup:

public void ConfigureServices(IServiceCollection services)
{
    //...       
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = "localhost:6379";
    });
}

3 - Consumir cache:

[Route("api/[controller]")]
[ApiController]
public class CidadaoController : ControllerBase
{

	private readonly ICidadaoRepository<Cidadao> cidadaoRepository;
    private readonly IDistributedCache _cache;
    private ILogger<EmployeeController> logger;

    public CidadaoController(
    	ICidadaoRepository<Cidadao> cidadaoRepository,
        IDistributedCache cache,
        ILogger<EmployeeController> logger
    )
    {
        this.cidadaoRepository = cidadaoRepository;
        this.cache = cache;
        this.logger = logger;
    }

    [HttpGet]
    public async Task<IActionResult> Index()
    {
    
    	var cacheKey = "listaCidadoes";
   		var cidadoes = new List<Cidadao>();
        var json = await cache.GetStringAsync(cacheKey);
        
        if(json != null)
        {
            products = JsonSerializer.Deserialize<List<Cidadao>>(json);
        }  
        else
        {
            _logger.Log(LogLevel.Information, "Nenhum cidadão encontrado no cache Redis. Buscando do banco de dados...");

            cidadoes = cidadaoRepository.GetAll();

            _logger.Log(LogLevel.Information, "Salvando cidadões no cache Redis...");
            
            json = JsonSerializer.Serialize<List<Cidadao>>(cidadoes);
            await _cache.SetStringAsync(cacheKey, json);
            
        }

        return Ok(cidadoes);
        
    }
    
}


IN-MEMORY CACHE

O cache em memória em ASP.NET Core uma forma simples e nativa de cache em que o aplicativo armazena dados na memória do servidor web. Isso é baseado na interface que representa um objeto de cache armazenado na memória do aplicativo.

Exemplo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
}
[Route("api/[controller]")]
[ApiController]
public class CidadaoController : ControllerBase
{
    private const string cacheKey = "listaCidadoes";
    private readonly ICidadaoRepository<Cidadao> cidadaoRepository;
    private IMemoryCache cache;
    private ILogger<EmployeeController> logger;

    public CidadaoController(
    	ICidadaoRepository<Cidadao> cidadaoRepository,
        IMemoryCache cache,
        ILogger<EmployeeController> logger
    )
    {
        this.cidadaoRepository = cidadaoRepository;
        this.cache = cache;
        this.logger = logger;
    }

    [HttpGet]
    public async Task<IActionResult> Index()
    {
    
        if (_cache.TryGetValue(cacheKey, out IEnumerable<Cidadadao> cidadoes))
        {
            _logger.Log(LogLevel.Information, "Nenhum cidadão encontrado no cache.");
        }
        else
        {
            _logger.Log(LogLevel.Information, "Nenhum cidadão encontrado em cache. Buscando do banco de dados...");

            cidadoes = cidadaoRepository.GetAll();

            _logger.Log(LogLevel.Information, "Salvando cidadões no cache...");
            
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetSlidingExpiration(TimeSpan.FromSeconds(60))
                    .SetAbsoluteExpiration(TimeSpan.FromSeconds(3600))
                    .SetPriority(CacheItemPriority.Normal)
                    .SetSize(1024);

            _cache.Set(cacheKey, cidadoes, cacheEntryOptions);
            
        }

        return Ok(cidadoes);
    }
    
}

Esta técnica de cache tem dois pontos importantes à serem levados em conta antes de uma possível implementação, a memória e a escalabilidade

Como estamos colocando os dados de cache na memória do servidor web, quanto mais dados forem inclusos em cache, mais memória será consumida pela aplicação em execução. O ASP.NET fica responsável por gerenciar a memória e desaloca-la caso necessário.