Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 43

Implementing Redis Caching in Aspire with Aspire.StackExchange.Redis

Recommended resources

Olá! Na lição anterior, desvendamos a teoria por trás de como o .NET Aspire descobre e injeta configurações para componentes, com base no Padrão Options do ASP.NET Core. Vimos como o AppHost orquestra os recursos e como os pacotes de componentes nos serviços consomem essa configuração de forma transparente.

Hoje, vamos colocar essa teoria em prática. O objetivo desta lição é integrar nosso primeiro e um dos mais comuns componentes em uma aplicação distribuída: um cache. Você aprenderá a integrar o Redis em uma aplicação Aspire usando o componente Aspire.StackExchange.Redis. Este é um passo fundamental para construir aplicações de back-end performáticas e escaláveis.

1. Redis e o Padrão de Cache-Aside

Antes de mergulharmos no código, vamos contextualizar. O Redis é um armazenamento de estrutura de dados em memória, de código aberto, usado como banco de dados, cache, agente de mensagens e muito mais. Sua principal vantagem é a velocidade, pois opera primariamente na RAM.

Redis Explained Infographic
Este infográfico oferece uma visão geral do Redis, incluindo suas configurações, mecanismos de persistência e os diversos tipos de dados que suporta. Para nossa lição, focaremos em seu uso como um cache.

Em aplicações de back-end, um dos usos mais comuns do Redis é implementar o padrão de cache cache-aside. A lógica é simples:

  1. Quando um dado é solicitado, a aplicação primeiro verifica se ele existe no cache (Redis).
  2. Cache Hit: Se o dado está no cache, ele é retornado diretamente, evitando uma consulta custosa ao banco de dados.
  3. Cache Miss: Se o dado não está no cache, a aplicação o busca na fonte de dados principal (como um banco de dados PostgreSQL), retorna-o ao cliente e, crucialmente, armazena-o no cache para futuras solicitações.

Este padrão reduz drasticamente a carga sobre o banco de dados e diminui a latência das respostas para dados frequentemente acessados. Conceitualmente, nossa arquitetura se parecerá com isto:

Aspire Starter Architecture with Redis
Este diagrama ilustra a arquitetura que vamos construir: um serviço de API que utiliza um cache Redis para otimizar o acesso aos dados, servindo um frontend (ou qualquer outro cliente).

2. A Integração em Dois Passos

Lembrando da lição anterior, a integração de um componente Aspire envolve dois projetos:

  1. O Projeto AppHost: Onde declaramos os recursos da nossa aplicação. Aqui, diremos ao Aspire: "minha aplicação precisa de um contêiner Redis".
  2. O Projeto de Serviço (API): Onde consumimos os recursos. Aqui, diremos à nossa API: "use aquele contêiner Redis que o AppHost configurou para você como um cache distribuído".

Vamos executar esses dois passos.


3. Passo 1: Configurando o Recurso no AppHost

Primeiro, precisamos informar ao orquestrador do Aspire sobre nossa necessidade de um Redis.

Stack Exchange Redis caching overview - Aspire

A documentação oficial da Microsoft detalha a integração de hospedagem do Redis. Ela é a referência principal para entender as capacidades do componente no AppHost.

Leia as seções "Hosting integration" e "Add Redis resource". Preste atenção em como o método AddRedis é usado para adicionar o recurso. Note também as extensões úteis como WithRedisCommander, que adiciona uma UI para inspecionar o cache, algo muito útil durante o desenvolvimento.

Para colocar isso em prática, siga estes passos no seu projeto:

  1. Adicionar o pacote NuGet: No seu projeto AppHost, instale o pacote de hospedagem do Redis.

    dotnet add package Aspire.Hosting.Redis
    
  2. Modificar o Program.cs do AppHost: Declare o recurso Redis e estabeleça a referência com o serviço que o utilizará.

    // Em AppHost/Program.cs
    
    var builder = DistributedApplication.CreateBuilder(args);
    
    // 1. Adiciona um contêiner Redis como um recurso chamado "cache"
    var cache = builder.AddRedis("cache");
    
    // Adiciona o projeto da API
    var apiservice = builder.AddProject<Projects.MyAspireApp_ApiService>("apiservice")
                            // 2. Informa que a API tem uma referência para o cache.
                            // Isso injetará a connection string do Redis no ambiente da API.
                            .WithReference(cache);
    
    // ... (outros serviços, como o frontend)
    
    builder.Build().Run();
    

O nome "cache" que passamos para AddRedis é o identificador chave. É através deste nome que o Aspire conectará o recurso no AppHost ao cliente no serviço.


4. Passo 2: Configurando o Cliente no Serviço da API

Agora que o AppHost conhece o Redis, precisamos configurar nossa API para usá-lo. Isso envolve instalar o pacote cliente e registrá-lo no contêiner de injeção de dependência (DI).

Para esta tarefa, usaremos o pacote Aspire.StackExchange.Redis.DistributedCaching. Ele se integra perfeitamente com a abstração IDistributedCache do ASP.NET Core, com a qual você, como desenvolvedor de back-end .NET, provavelmente já está familiarizado.

.NET Aspire Tutorial For Beginners

Este vídeo de Julio Casal demonstra de forma prática todo o processo de integração, começando pela configuração do AppHost e seguindo para a configuração da API. É uma excelente demonstração visual dos passos que estamos seguindo.

Assista ao trecho de 01:05:35 a 01:08:45. Observe como ele: Adiciona a referência ao Redis no AppHost (builder.AddRedis, .WithReference). Instala o pacote cliente na API. Chama builder.AddRedisDistributedCache("Redis") no Program.cs da API, ressaltando a importância de o nome corresponder ao do AppHost.

Resumindo os passos para o seu projeto de API:

  1. Adicionar o pacote NuGet: No seu projeto de API (ApiService), instale o pacote cliente para cache distribuído.
    dotnet add package Aspire.StackExchange.Redis.DistributedCaching
    
  2. Modificar o Program.cs da API: Chame o método de extensão do Aspire para registrar o IDistributedCache.
    // Em ApiService/Program.cs
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Adiciona os padrões de serviço do Aspire (telemetria, health checks, etc.)
    builder.AddServiceDefaults();
    
    // 1. Registra o cliente de cache distribuído do Redis.
    // O nome "cache" DEVE corresponder ao nome usado no AppHost.
    builder.AddRedisDistributedCache("cache");
    
    // ... (restante da configuração de serviços)
    
    var app = builder.Build();
    
    // Mapeia os endpoints padrão do Aspire (ex: /health)
    app.MapDefaultEndpoints();
    
    // ... (restante do pipeline)
    
    app.Run();
    

Pronto! Com essas duas linhas de código, o Aspire cuida de:

  • Ler a connection string injetada pelo AppHost.
  • Configurar o cliente StackExchange.Redis.
  • Registrar uma implementação de IDistributedCache no contêiner de DI.
  • Adicionar automaticamente health checks e telemetria (tracing e métricas) para cada chamada ao Redis.
Teste seu entendimento!

Você configurou seu AppHost com builder.AddRedis("my-cache") e seu serviço de API com builder.AddRedisDistributedCache("default-cache"). Ao executar o aplicativo, sua API lança uma exceção informando que não consegue se conectar ao Redis. Qual é a causa mais provável desse erro?

Show answer

A causa mais provável é a incompatibilidade entre os nomes dos recursos. O nome é a "chave" que liga a definição do recurso no AppHost ("my-cache") à configuração do cliente no serviço ("default-cache"). Para que o Aspire injete a connection string correta, esses nomes devem ser idênticos.


5. Implementando o Padrão Cache-Aside

Com a configuração completa, o uso do cache no código da sua aplicação é exatamente como você faria com IDistributedCache em qualquer aplicação ASP.NET Core.

.NET Aspire Tutorial For Beginners

Continuando com o vídeo anterior, esta seção mostra como modificar um endpoint de API para implementar o padrão cache-aside. É uma aplicação prática e direta do que acabamos de configurar.

Assista ao trecho de 01:08:45 a 01:12:32. Preste atenção à lógica de programação: Injetar IDistributedCache no endpoint. Tentar obter o item do cache primeiro (cache.GetStringAsync). Se for um cache miss (nulo), buscar no banco de dados. Armazenar o resultado no cache (cache.SetStringAsync) com um tempo de expiração antes de retornar.

Aqui está um exemplo conciso de como isso ficaria em um endpoint de API Mínima:

// Em ApiService, onde os endpoints são mapeados

app.MapGet("/products/{id}", async (int id, IDistributedCache cache, ProductDbContext dbContext) =>
{
    // 1. Define uma chave única para o item no cache.
    string cacheKey = $"product-{id}";

    // 2. Tenta obter o produto do cache.
    string? cachedProductJson = await cache.GetStringAsync(cacheKey);

    if (cachedProductJson is not null)
    {
        // Cache Hit: Deserializa e retorna o produto do cache.
        var cachedProduct = JsonSerializer.Deserialize<Product>(cachedProductJson);
        return Results.Ok(cachedProduct);
    }

    // 3. Cache Miss: Busca o produto no banco de dados.
    var productFromDb = await dbContext.Products.FindAsync(id);

    if (productFromDb is null)
    {
        return Results.NotFound();
    }

    // 4. Armazena o produto no cache para a próxima vez.
    var options = new DistributedCacheEntryOptions()
        .SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
    
    string productToCacheJson = JsonSerializer.Serialize(productFromDb);
    await cache.SetStringAsync(cacheKey, productToCacheJson, options);

    return Results.Ok(productFromDb);
});

6. Verificação e Observabilidade no Dashboard

A verdadeira "mágica" do Aspire se revela quando executamos a aplicação e olhamos para o Dashboard. É aqui que vemos os benefícios da telemetria integrada.

.NET Aspire Tutorial For Beginners

Esta seção final do vídeo é crucial. Ela mostra como usar o Aspire Dashboard para verificar se a integração com o Redis está funcionando e, mais importante, para observar o impacto no desempenho da aplicação através de logs e traces distribuídos.

Assista ao trecho de 01:12:32 a 01:19:38. Foque em observar: O contêiner Redis aparecendo na lista de Recursos. Os logs estruturados mostrando as mensagens de "cache miss". A diferença fundamental nos traces distribuídos entre a primeira chamada (cache miss), que inclui a consulta ao banco de dados, e a segunda chamada (cache hit), que é muito mais rápida e vai apenas até o Redis.

Ao executar seu AppHost e fazer requisições ao seu endpoint:

  • Primeira Requisição (Cache Miss): Vá para a aba Traces no Aspire Dashboard. Você verá um trace distribuído que mostra a requisição chegando na sua API, fazendo uma chamada ao Redis (que falha em encontrar o item), depois fazendo uma chamada ao seu banco de dados e, finalmente, outra chamada ao Redis para salvar o item.
  • Segunda Requisição (Cache Hit): Faça a mesma requisição novamente. Agora, o trace será muito mais simples e rápido. Ele mostrará a requisição chegando à API e fazendo uma única chamada ao Redis para obter o dado. O banco de dados não será contatado.

Essa visualização lado a lado de um cache hit vs. um cache miss é uma ferramenta de diagnóstico e aprendizado incrivelmente poderosa, fornecida "de graça" pelo Aspire.


Conclusão

Nesta lição, demos nosso primeiro passo prático na construção de uma aplicação cloud-native com Aspire, aplicando a teoria da lição anterior.

Principais Pontos:

  • A integração de um componente como o Redis é um processo de dois passos: declarar o recurso no AppHost (builder.AddRedis) e configurar o cliente no serviço (builder.AddRedisDistributedCache).
  • O nome do recurso é a chave que conecta a configuração do AppHost ao cliente no serviço.
  • Usando o pacote Aspire.StackExchange.Redis.DistributedCaching, podemos injetar a abstração padrão IDistributedCache para implementar padrões de cache como o cache-aside.
  • O Aspire Dashboard é essencial para verificar a integração e observar o comportamento da aplicação através de logs estruturados e, mais importante, traces distribuídos, que visualizam o fluxo de uma requisição através dos serviços.

Agora que você dominou a integração de um componente de cache, estamos prontos para abordar a persistência de dados. Na próxima lição, aplicaremos exatamente os mesmos princípios para integrar um banco de dados PostgreSQL usando o componente Aspire.Npgsql.