Dela via


HybridCache-bibliotek i ASP.NET Core

Viktig

HybridCache är för närvarande fortfarande i förhandsversion men kommer att släppas helt efter .NET 9.0 i en framtida mindre version av .NET-tillägg.

Den här artikeln beskriver hur du konfigurerar och använder HybridCache-biblioteket i en ASP.NET Core-app. En introduktion till biblioteket finns i avsnittet HybridCache i översikten över cachelagring.

Hämta biblioteket

Installera Microsoft.Extensions.Caching.Hybrid-paketet.

dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"

Registrera tjänsten

Lägg till HybridCache-tjänsten i beroendeinmatning (DI) container genom att anropa AddHybridCache:

// Add services to the container.
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthorization();

builder.Services.AddHybridCache();

Föregående kod registrerar HybridCache-tjänsten med standardalternativ. Registrerings-API:et kan också konfigurera alternativ och serialisering.

Hämta och lagra cache-objekt

Tjänsten HybridCache tillhandahåller en GetOrCreateAsync-metod med två överlagringar, där en tar en nyckel och:

  • En fabriksmetod.
  • Tillstånd och en fabriksmetod.

Metoden använder nyckeln för att försöka hämta objektet från den primära cachen. Om objektet inte hittas i den primära cachen (en cachemiss) kontrollerar det sedan den sekundära cachen om en har konfigurerats. Om den inte hittar data där (en annan cachemiss) anropas fabriksmetoden för att hämta objektet från datakällan. Objektet lagras sedan i både primära och sekundära cacheminnen. Fabriksmetoden anropas aldrig om objektet hittas i den primära eller sekundära cachen (en cacheträff).

Tjänsten HybridCache ser till att endast en samtidig anropare för en viss nyckel anropar fabriksmetoden och alla andra anropare väntar på resultatet av det anropet. Den CancellationToken som skickas till GetOrCreateAsync representerar den kombinerade annulleringen av alla samtidiga anropare.

Den huvudsakliga GetOrCreateAsync-överbelastningen

Tillståndslös överbelastning av GetOrCreateAsync rekommenderas för de flesta scenarier. Koden som ska anropas är relativt enkel. Här är ett exempel:

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;
    }
}

Vägledning för cachenyckel

Den key som skickas till GetOrCreateAsync måste unikt identifiera de data som cachelagras:

  • När det gäller de identifierarvärden som används för att hämta dessa data från källan.
  • När det gäller andra data som cachelagras i programmet.

Båda typerna av unikhet säkerställs vanligtvis med hjälp av strängsammanfogning för att göra en enda nyckelsträng bestående av olika delar sammanfogade i en sträng. Till exempel:

cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);

eller

cache.GetOrCreateAsync($"user_prefs_{userId}", ...);

Det är anroparens ansvar att se till att ett nyckelschema är giltigt och inte kan orsaka att data blir förvirrade.

Vi rekommenderar att du inte använder extern användarinmatning i cache-nyckeln. Använd till exempel inte råa string värden från ett användargränssnitt som en del av en cachenyckel. Sådana nycklar kan tillåta försök till skadlig åtkomst eller användas i en överbelastningsattack genom att mätta cachen med data som har meningslösa nycklar som genereras från slumpmässiga strängar. I de föregående giltiga exemplen är beställning-data och användarpreferens-data tydligt åtskilda.

  • orderid och userId är internt genererade identifierare.
  • region kan vara en uppräkning eller sträng från en fördefinierad lista över kända regioner.

Det finns ingen betydelse för token som / eller _. Hela nyckelvärdet behandlas som en ogenomskinlig identifieringssträng. I det här fallet kan du utelämna / och _ utan att ändra hur cachen fungerar, men en avgränsare används vanligtvis för att undvika tvetydighet , till exempel $"order{customerId}{orderId}" kan orsaka förvirring mellan:

  • customerId 42 med orderId 123
  • customerId 421 med orderId 23

(som båda skulle generera cachenyckeln order42123)

Den här vägledningen gäller även för alla string-baserade cache-API:et, till exempel HybridCache, IDistributedCacheoch IMemoryCache.

Observera att den infogade interpolerade strängsyntaxen ($"..." i föregående exempel på giltiga nycklar) finns direkt i GetOrCreateAsync-anropet. Den här syntaxen rekommenderas när du använder HybridCacheeftersom den möjliggör planerade framtida förbättringar som kringgår behovet av att allokera en string för nyckeln i många scenarier.

Ytterligare viktiga överväganden

  • Nycklar kan begränsas till giltiga maximala längder. Standardimplementeringen för HybridCache (via AddHybridCache(...)) begränsar till exempel nycklar till 1 024 tecken som standard. Det talet kan konfigureras via HybridCacheOptions.MaximumKeyLength, med längre nycklar som kringgår cachemekanismerna för att förhindra mättnad.
  • Nycklarna måste vara giltiga Unicode-sekvenser. Om ogiltiga Unicode-sekvenser skickas är beteendet odefinierat.
  • När du använder en sekundär cache som inte är bearbetad, till exempel IDistributedCache, kan den specifika serverdelsimplementeringen införa ytterligare begränsningar. Som ett hypotetiskt exempel kan en viss backend använda skiftlägesokänslig nyckellogik. Standard-HybridCache (via AddHybridCache(...)) identifierar det här scenariot för att förhindra förvirringsattacker, men det kan fortfarande leda till att motstridiga nycklar skrivs över eller avlägsnas tidigare än förväntat.

Alternativ GetOrCreateAsync överbelastning

Den alternativa överlagringen kan minska vissa kostnader från insamlade variabler och återanrop per instans, men på bekostnad av mer komplex kod. I de flesta scenarier uppväger prestandaökningen inte kodkomplexiteten. Här är ett exempel som använder alternativ överlagring:

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;
    }
}

Metoden SetAsync

I många scenarier är GetOrCreateAsync det enda API som behövs. Men HybridCache har också SetAsync för att lagra ett objekt i cacheminnet utan att försöka hämta det först.

Ta bort cacheposter med hjälp av nyckel

När underliggande data för en cachepost ändras innan den upphör att gälla tar du bort posten explicit genom att anropa RemoveAsync med nyckeln till posten. Med en överbelastning kan du ange en samling nyckelvärden.

När en post tas bort tas den bort från både de primära och sekundära cacheminnena.

Ta bort cacheposter efter tagg

Viktig

Den här funktionen är fortfarande under utveckling. Om du försöker ta bort poster efter tagg ser du att det inte har någon effekt.

Taggar kan användas för att gruppera cacheposter och ogiltigförklara dem tillsammans.

Ange taggar när du anropar GetOrCreateAsync, enligt följande exempel:

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;
    }
}

Ta bort alla poster för en angiven tagg genom att anropa RemoveByTagAsync med taggvärdet. Med en överlagring kan du ange en samling taggvärden.

När en post tas bort tas den bort från både de primära och sekundära cacheminnena.

Alternativ

Metoden AddHybridCache kan användas för att konfigurera globala standardvärden. I följande exempel visas hur du konfigurerar några av de tillgängliga alternativen:

// 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)
        };
    });

Metoden GetOrCreateAsync kan också ta ett HybridCacheEntryOptions objekt för att åsidosätta de globala standardvärdena för en specifik cachepost. Här är ett exempel:

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;
    }
}

Mer information om alternativen finns i källkoden:

Gränser

Med följande egenskaper för HybridCacheOptions kan du konfigurera gränser som gäller för alla cacheposter:

  • MaximumPayloadBytes – Maximal storlek på en cachepost. Standardvärdet är 1 MB. Försök att lagra värden över den här storleken loggas och värdet lagras inte i cacheminnet.
  • MaximumKeyLength – Maximal längd på en cachenyckel. Standardvärdet är 1 024 tecken. Försök att lagra värden över den här storleken loggas och värdet lagras inte i cacheminnet.

Serialisering

Användning av ett sekundärt cacheminne utan process kräver serialisering. Serialisering konfigureras som en del av registreringen av HybridCache-tjänsten. Typspecifika och allmänna serialiserare kan konfigureras via metoderna AddSerializer och AddSerializerFactory, länkade från AddHybridCache-anropet. Som standard hanterar biblioteket string och byte[] internt och använder System.Text.Json för allt annat. HybridCache kan också använda andra serialiserare, till exempel protobuf eller XML.

I följande exempel konfigureras tjänsten för att använda en typspecifik protobuf-serialiserare:

// 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>>();

I följande exempel konfigureras tjänsten för att använda en protobuf-serialiserare för generell användning som kan hantera många protobuf-typer:

// 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>();

Den sekundära cachen kräver ett datalager, till exempel Redis eller SqlServer. Så här använder du Azure Cache for Redis, till exempel:

  • Installera Microsoft.Extensions.Caching.StackExchangeRedis-paketet.

  • Skapa en instans av Azure Cache for Redis.

  • Hämta en anslutningssträng som ansluter till Redis-instansen. Leta upp anslutningssträngen genom att välja Visa åtkomstnycklar på sidan Översikt i Azure-portalen.

  • Lagra anslutningssträngen i appens konfiguration. Använd till exempel en fil med användarhemligheter som ser ut som följande JSON, med anslutningssträngen i avsnittet ConnectionStrings. Ersätt <the connection string> med den faktiska anslutningssträngen:

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • Registrera i DI den IDistributedCache implementering som Redis-paketet tillhandahåller. Det gör du genom att anropa AddStackExchangeRedisCacheoch skicka anslutningssträngen. Till exempel:

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • Redis-IDistributedCache-implementeringen är nu tillgänglig från appens DI-container. HybridCache använder den som sekundär cache och använder serialiseraren som konfigurerats för den.

Mer information finns i appen HybridCache serialiseringsexempel.

Cachelagring

Som standard använder HybridCacheMemoryCache för sin primära cachelagring. Cacheposter lagras i processen, så varje server har en separat cache som går förlorad när serverprocessen startas om. För sekundär out-of-process-lagring, till exempel Redis eller SQL Server, använder HybridCacheden konfigurerade IDistributedCache implementeringen, om sådan finns. Men även utan en IDistributedCacheimplementering tillhandahåller HybridCache-tjänsten fortfarande processbaserad cachelagring och stampede-skydd.

Not

När cacheposter ogiltigförklaras genom nyckel eller taggar, ogiltigförklaras de både på den aktuella servern och i den sekundära utomprocesslagringen. Minnesintern cache på andra servrar påverkas dock inte.

Optimera prestanda

Du kan optimera prestanda genom att konfigurera HybridCache att återanvända objekt och undvika byte[] allokeringar.

Återanvända objekt

Genom att återanvända instanser kan HybridCache minska kostnaderna för PROCESSOR- och objektallokeringar som är associerade med deserialisering per anrop. Detta kan leda till prestandaförbättringar i scenarier där cachelagrade objekt är stora eller används ofta.

I vanlig befintlig kod som använder IDistributedCacheresulterar varje hämtning av ett objekt från cachen i deserialisering. Det här beteendet innebär att varje samtidig anropare får en separat instans av objektet, som inte kan interagera med andra instanser. Resultatet är trådsäkerhet eftersom det inte finns någon risk för samtidiga ändringar av samma objektinstans.

Eftersom mycket HybridCache användning kommer att anpassas från befintlig IDistributedCache kod, bevarar HybridCache det här beteendet som standard för att undvika samtidighetsbuggar. Objekt är dock i sig trådsäkra om:

  • De är oföränderliga typer.
  • Koden ändrar dem inte.

I sådana fall, informera HybridCache om att det är säkert att återanvända exemplar genom att:

  • Markera typen som sealed. Nyckelordet sealed i C# innebär att klassen inte kan ärvas.
  • Tillämpa attributet [ImmutableObject(true)] på typen. Attributet [ImmutableObject(true)] anger att objektets tillstånd inte kan ändras när det har skapats.

Undvik byte[] allokeringar

HybridCache tillhandahåller även valfria API:er för IDistributedCache implementeringar för att undvika byte[] allokeringar. Den här funktionen implementeras av förhandsversionerna av paketen Microsoft.Extensions.Caching.StackExchangeRedis och Microsoft.Extensions.Caching.SqlServer. Mer information finns i IBufferDistributedCache Här är .NET CLI-kommandona för att installera paketen:

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer

Anpassade HybridCache-implementeringar

En konkret implementering av den HybridCache abstrakta klassen ingår i det delade ramverket och tillhandahålls via beroendeinmatning. Men utvecklare är välkomna att tillhandahålla anpassade implementeringar av API:et.

Kompatibilitet

HybridCache-biblioteket stöder äldre .NET-körningar, ned till .NET Framework 4.7.2 och .NET Standard 2.0.

Ytterligare resurser

Mer information om HybridCachefinns i följande resurser: