Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo explica como configurar e usar a biblioteca do HybridCache
em um aplicativo ASP.NET Core. Para ver uma introdução à biblioteca, confira a seção HybridCache
da visão geral do cache.
Obter a biblioteca
Instale o pacote Microsoft.Extensions.Caching.Hybrid
.
dotnet add package Microsoft.Extensions.Caching.Hybrid
Registrar o serviço
Adicione o serviço HybridCache
ao contêiner de DI (injeção de dependência) chamando AddHybridCache:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
builder.Services.AddHybridCache();
O código anterior registra o serviço HybridCache
com opções padrão. A API de registro também pode configurar opções e serialização.
Obter e armazenar entradas do cache
O serviço HybridCache
fornece um método GetOrCreateAsync com duas sobrecargas, usando uma chave e:
- Um método de fábrica.
- Estado e um método de fábrica.
O método usa a chave para tentar recuperar o objeto do cache primário. Se o item não for encontrado no cache primário (uma perda no cache), ele verificará o cache secundário, caso ele esteja configurado. Se ele não encontrar os dados nele (outra perda no cache), ele chamará o método de fábrica para obter o objeto da fonte de dados. Em seguida, ele armazenará o objeto nos caches primário e secundário. O método de fábrica nunca será chamado se o objeto for encontrado no cache primário ou secundário (uma ocorrência no cache).
O serviço HybridCache
garante que apenas um chamador simultâneo para determinada chave chame o método de fábrica e todos os outros chamadores aguardem pelo resultado dessa chamada. O CancellationToken
transmitido para GetOrCreateAsync
representa o cancelamento combinado de todos os chamadores simultâneos.
A sobrecarga principal GetOrCreateAsync
A sobrecarga sem estado GetOrCreateAsync
é recomendada para a maioria dos cenários. O código usado para chamá-la é relativamente simples. Veja um exemplo:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Diretrizes de chave de cache
O key
passado para GetOrCreateAsync
deve identificar exclusivamente os dados que estão sendo armazenados em cache:
- No que diz respeito aos valores dos identificadores usados para recuperar esses dados de sua origem.
- Em termos de outros dados armazenados em cache no aplicativo.
Ambos os tipos de exclusividade geralmente são garantidos usando concatenação de strings para criar uma chave única composta de diferentes partes concatenadas em uma única string. Por exemplo:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
Ou
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
É responsabilidade do chamador garantir que um esquema de chave seja válido e não possa fazer com que os dados fiquem confusos.
Evite usar a entrada do usuário externo diretamente em chaves de cache. Por exemplo, não use cadeias de caracteres brutas de interfaces do usuário como chaves de cache. Isso pode expor seu aplicativo a riscos de segurança, como acesso não autorizado ou ataques de negação de serviço causados pela inundação do cache com chaves aleatórias ou sem sentido. Nos exemplos válidos anteriores, os dados de ordem e preferência do usuário são claramente separados e usam identificadores confiáveis:
-
orderid
euserId
são identificadores gerados internamente. -
region
pode ser uma enumeração ou cadeia de caracteres de uma lista predefinida de regiões conhecidas.
Nenhum significado é colocado em tokens como /
ou _
. Todo o valor da chave é tratado como uma cadeia de caracteres de identificação opaca. Nesse caso, você pode omitir o /
e _
sem nenhuma alteração na maneira como o cache funciona, mas um delimitador geralmente é usado para evitar ambiguidade - por exemplo, $"order{customerId}{orderId}"
pode causar confusão entre:
-
customerId
42 comorderId
123 -
customerId
421 comorderId
23
Ambos os exemplos anteriores gerariam a chave order42123
de cache.
Essas diretrizes se aplicam igualmente a qualquer API de cache baseada em string
, como HybridCache
, IDistributedCache
e IMemoryCache
.
Observe que a sintaxe de cadeia de caracteres interpolada em linha ($"..."
nos exemplos anteriores de chaves válidas) está diretamente dentro da chamada GetOrCreateAsync
. Essa sintaxe é recomendada ao usar HybridCache
, pois permite melhorias futuras planejadas que ignoram a necessidade de alocar um string
para a chave em muitos cenários.
Considerações importantes adicionais
- As chaves podem ser restritas a comprimentos máximos válidos. Por exemplo, a implementação de
HybridCache
padrão (viaAddHybridCache(...)
) restringe as chaves a 1.024 caracteres por padrão. Esse número é configurável por meio deHybridCacheOptions.MaximumKeyLength
, com chaves mais longas ignorando os mecanismos de cache para evitar a saturação. - As chaves devem ser sequências Unicode válidas. Se sequências Unicode inválidas forem passadas, o comportamento será indefinido.
- Ao usar um cache secundário fora do processo, como
IDistributedCache
, a implementação de back-end pode impor restrições adicionais. Como um exemplo hipotético, um back-end específico pode usar uma lógica de chave que não diferencia maiúsculas de minúsculas. O padrãoHybridCache
(viaAddHybridCache(...)
) detecta esse cenário para evitar ataques de confusão ou ataques de alias (usando igualdade de cadeia de caracteres bit a bit). No entanto, esse cenário ainda pode resultar em chaves conflitantes sendo substituídas ou removidas mais cedo do que o esperado.
A sobrecarga alternativa GetOrCreateAsync
A sobrecarga alternativa pode reduzir um pouco da sobrecarga das variáveis capturadas e dos retornos de chamada por instância, mas em detrimento de um código mais complexo. Para a maioria dos cenários, o aumento de desempenho não supera a complexidade do código. Este é um exemplo que usa a sobrecarga alternativa:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
(name, id, obj: this),
static async (state, token) =>
await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
O método SetAsync
Em muitos cenários, GetOrCreateAsync
é a única API necessária. Porém, HybridCache
também tem SetAsync para armazenar um objeto no cache sem tentar recuperá-lo primeiro.
Remover entradas de cache por chave
Quando os dados subjacentes de uma entrada de cache forem alterados antes de expirarem, remova a entrada explicitamente chamando RemoveAsync com a chave para a entrada. Uma sobrecarga permite que você especifique uma coleção de valores de chave.
Quando uma entrada é removida, ela é removida dos caches primário e secundário.
Remover entradas de cache por marcação
As tags podem ser usadas para agrupar as entradas de cache e invalidá-las coletivamente.
Defina as marcas ao chamar GetOrCreateAsync
, conforme mostrado no seguinte exemplo:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Remova todas as entradas de uma etiqueta especificada, utilizando RemoveByTagAsync com o valor da etiqueta. Uma sobrecarga permite que você especifique uma coleção de valores de marcações.
Nem IMemoryCache
nem IDistributedCache
dão suporte direto para o conceito de tags, portanto, a invalidação baseada em tags é apenas uma operação lógica. Ele não remove ativamente os valores do cache local ou distribuído. Em vez disso, garante que, ao receber dados com essas tags, os dados sejam tratados como um cache-miss tanto do cache local quanto do remoto. Os valores expiram de IMemoryCache
e IDistributedCache
da maneira usual com base no tempo de vida configurado.
Removendo todas as entradas de cache
A marca de asterisco (*
) é reservada como curinga e não é permitida em relação a valores individuais. A chamada RemoveByTagAsync("*")
tem o efeito de invalidar todos osHybridCache
dados, mesmo os dados que não têm marcas. Assim como acontece com marcas individuais, essa é uma operação lógica e os valores individuais continuam a existir até expirarem naturalmente. Não há suporte para correspondências no estilo Glob. Por exemplo, você não pode usar RemoveByTagAsync("foo*")
para remover tudo começando com foo
.
Considerações adicionais de etiqueta
- O sistema não limita o número de marcas que você pode usar, mas grandes conjuntos de marcas podem afetar negativamente o desempenho.
- As tags não podem estar vazias, conter apenas espaços em branco ou o valor reservado
*
.
Opções
O método AddHybridCache
pode ser usado para configurar padrões globais. O seguinte exemplo mostra como configurar algumas das opções disponíveis:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024;
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
O método GetOrCreateAsync
também pode usar um objeto HybridCacheEntryOptions
para substituir os padrões globais de uma entrada do cache específica. Veja um exemplo:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Para obter mais informações sobre as opções, confira o código-fonte:
- HybridCacheOptions classe.
- HybridCacheEntryOptions classe.
Limites
As seguintes propriedades de HybridCacheOptions
permitem configurar limites que se aplicam a todas as entradas do cache:
- MaximumPayloadBytes – Tamanho máximo de uma entrada do cache. O valor padrão é 1 MB. As tentativas de armazenar valores desse tamanho são registradas em log e o valor não é armazenado em cache.
- MaximumKeyLength – Comprimento máximo de uma chave do cache. O valor padrão é 1.024 caracteres. As tentativas de armazenar valores desse tamanho são registradas em log e o valor não é armazenado em cache.
Serialização
O uso de um cache secundário fora do processo requer serialização. A serialização é configurada como parte do registro do serviço HybridCache
. Serializadores específicos de tipo e de uso geral podem ser configurados por meio dos métodos AddSerializer e AddSerializerFactory encadeados da chamada AddHybridCache
. Por padrão, a biblioteca processa string
e byte[]
internamente e usa System.Text.Json
para todo o restante.
HybridCache
também pode usar outros serializadores, como protobuf ou XML.
O seguinte exemplo configura o serviço para usar um serializador protobuf específico do tipo:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializer<SomeProtobufMessage,
GoogleProtobufSerializer<SomeProtobufMessage>>();
O seguinte exemplo configura o serviço para usar um serializador protobuf de uso geral que pode lidar com muitos tipos de protobuf:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();
O cache secundário requer um armazenamento de dados, como Redis ou SqlServer. Para usar o Azure Cache para Redis, por exemplo:
Instale o pacote
Microsoft.Extensions.Caching.StackExchangeRedis
.Crie uma instância do Cache do Azure para Redis.
Obtenha uma cadeia de conexão que se conecte à instância do Redis. Localize a cadeia de conexão selecionando Mostrar chaves de acesso na página Visão geral no portal do Azure.
Armazene a cadeia de conexão na configuração do aplicativo. Por exemplo, use um arquivo de segredos do usuário semelhante ao JSON a seguir, com a cadeia de conexão na seção
ConnectionStrings
. Substitua<the connection string>
pela cadeia de conexão real:{ "ConnectionStrings": { "RedisConnectionString": "<the connection string>" } }
Registre na DI a implementação de
IDistributedCache
fornecida pelo pacote Redis. Para fazer isso, chameAddStackExchangeRedisCache
e passe a cadeia de conexão. Por exemplo:builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString"); });
A implementação
IDistributedCache
do Redis agora está disponível no contêiner de DI do aplicativo.HybridCache
o usa como o cache secundário e usa o serializador configurado para ele.
Para obter mais informações, consulte o aplicativo de exemplo de serialização HybridCache.
Armazenamento em cache
Por padrão, o HybridCache
usa MemoryCache para o armazenamento de cache primário. As entradas do cache são armazenadas em processo. Portanto, cada servidor tem um cache separado que é perdido sempre que o processo do servidor é reiniciado. Para o armazenamento secundário fora de processo, como o Redis ou o SQL Server, o HybridCache
usa a implementação de IDistributedCache
configurada, se houver. Porém, mesmo sem uma implementação de IDistributedCache
, o serviço HybridCache
ainda fornece cache em processo e proteção contra dispersão.
Nota
Ao invalidar entradas de cache por chave ou por marcas, elas são invalidadas no servidor atual e no armazenamento fora de processo secundário. No entanto, o cache na memória em outros servidores não é afetado.
Otimizar o desempenho
Para otimizar o desempenho, configure HybridCache
para reutilizar objetos e evitar alocações de byte[]
.
Reutilizar objetos
Ao reutilizar instâncias, HybridCache
pode reduzir a sobrecarga de alocações de CPU e objeto associadas à desserialização por chamada. Isso pode levar a aprimoramentos de desempenho em cenários em que os objetos armazenados em cache são grandes ou acessados com frequência.
No código existente típico que usa IDistributedCache
, cada recuperação de um objeto do cache resulta em desserialização. Esse comportamento significa que cada chamador simultâneo obtém uma instância separada do objeto, que não pode interagir com outras instâncias. O resultado é a segurança de thread, pois não há risco de modificações simultâneas na mesma instância de objeto.
Como grande parte do uso do HybridCache
será adaptado do código IDistributedCache
existente, HybridCache
preserva esse comportamento por padrão para evitar a introdução de bugs de simultaneidade. No entanto, os objetos serão inerentemente thread-safe se:
- São tipos imutáveis.
- O código não os modifica.
Nesses casos, informe HybridCache
que é seguro reutilizar instâncias fazendo pelo menos uma das seguintes alterações:
- Marcando o tipo como
sealed
. A palavra-chavesealed
em C# significa que a classe não pode ser herdada. - Aplicando o atributo
[ImmutableObject(true)]
ao tipo. O atributo[ImmutableObject(true)]
indica que o estado do objeto não pode ser alterado depois de criado.
Evitar alocações de byte[]
HybridCache
também fornece APIs opcionais para implementações de IDistributedCache
, para evitar alocações byte[]
. Esse recurso é implementado pelas versões prévias dos pacotes Microsoft.Extensions.Caching.StackExchangeRedis
e Microsoft.Extensions.Caching.SqlServer
. Para obter mais informações, consulte IBufferDistributedCache.
Aqui estão os comandos da CLI do .NET para instalar os pacotes:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
Implementações personalizadas do HybridCache
Uma implementação concreta da classe abstrata HybridCache
é incluída na estrutura compartilhada e é fornecida por meio de injeção de dependência. Mas os desenvolvedores são bem-vindos a fornecer ou consumir implementações personalizadas da API, por exemplo, o FusionCache.
Utilize Cache Híbrido com AOT Nativo
As seguintes considerações específicas do AOT Nativo se aplicam a HybridCache
:
Serialização
O AOT nativo não dá suporte à serialização baseada em reflexão de runtime. Se você armazenar em cache tipos personalizados, deverá usar geradores de código ou configurar explicitamente serializadores compatíveis com AOT, como a geração de código
System.Text.Json
.HybridCache
ainda está em desenvolvimento e simplificar a maneira de usá-lo com a AOT é uma alta prioridade para esse desenvolvimento. Para obter mais informações, consulte pull request dotnet/extensions#6475Aparagem
Verifique se todos os tipos que você armazena em cache são referenciados de uma maneira que os impeça de serem cortados pelo compilador AOT. O uso de geradores de origem para serialização ajuda com esse requisito. Para obter mais informações, consulte Suporte do ASP.NET Core para Native AOT.
Se você configurar a serialização e cortar corretamente, HybridCache
se comportará da mesma maneira no AOT Nativo como em aplicativos ASP.NET Core regulares.
Compatibilidade
A biblioteca HybridCache
dá suporte a runtimes mais antigos do .NET, até o .NET Framework 4.7.2 e o .NET Standard 2.0.
Recursos adicionais
Para obter mais informações, consulte o HybridCache
código-fonte