Delen via


HybridCache-bibliotheek in ASP.NET Core

Belangrijk

HybridCache is momenteel nog in preview, maar wordt volledig uitgebracht na .NET 9.0 in een toekomstige secundaire versie van .NET Extensions.

In dit artikel wordt uitgelegd hoe u de HybridCache-bibliotheek in een ASP.NET Core-app configureert en gebruikt. Zie de sectie HybridCache van het overzicht van cachingvoor een inleiding tot de bibliotheek.

De bibliotheek verkrijgen

Installeer het Microsoft.Extensions.Caching.Hybrid-pakket.

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

De service registreren

Voeg de HybridCache-service toe aan de afhankelijkheidsinjectie (DI) container door AddHybridCacheaan te roepen:

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

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

builder.Services.AddHybridCache();

De voorgaande code registreert de HybridCache-service met standaardopties. De registratie-API kan ook opties configureren en serialisatie.

Cachevermeldingen ophalen en opslaan

De HybridCache-service biedt een GetOrCreateAsync-methode met twee overloads, waarbij een sleutel wordt gebruikt en:

  • Een fabrieksmethode.
  • Staat en een fabrieksmethode.

De methode gebruikt de sleutel om het object op te halen uit de primaire cache. Als het item niet wordt gevonden in de primaire cache (een cache miss), wordt de secundaire cache gecontroleerd indien deze is geconfigureerd. Wanneer de gegevens daar niet worden gevonden (een andere cachemiss), wordt de factorymethode aangeroepen om het object uit de gegevensbron op te halen. Vervolgens wordt het object opgeslagen in zowel primaire als secundaire caches. De factory-methode wordt nooit aangeroepen als het object wordt gevonden in de primaire of secundaire cache (een cachetreffer).

De HybridCache-service zorgt ervoor dat slechts één gelijktijdige beller voor een bepaalde sleutel de factory-methode aanroept en alle andere bellers wachten op het resultaat van die aanroep. De CancellationToken die aan GetOrCreateAsync is doorgegeven vertegenwoordigt de gezamenlijke annulering van alle gelijktijdige bellers.

De belangrijkste GetOrCreateAsync overbelasting

De stateless overload van GetOrCreateAsync wordt aanbevolen voor de meeste scenario's. De code die moet worden aangeroepen, is relatief eenvoudig. Hier volgt een voorbeeld:

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

Richtlijnen voor cachesleutels

De key doorgegeven aan GetOrCreateAsync moeten de gegevens die in de cache worden opgeslagen, uniek identificeren:

  • In termen van de id-waarden die worden gebruikt om die gegevens op te halen uit de bron.
  • Wat betreft andere gegevens die in de cache van de toepassing zijn opgeslagen.

Beide typen uniekheid worden meestal gegarandeerd door tekenreekssamenvoeging te gebruiken om één sleuteltekenreeks te maken die bestaat uit verschillende onderdelen die zijn samengevoegd in één tekenreeks. Bijvoorbeeld:

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

of

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

Het is de verantwoordelijkheid van de beller om ervoor te zorgen dat een sleutelschema geldig is en dat gegevens niet in de war raken.

U wordt aangeraden geen externe gebruikersinvoer in de cachesleutel te gebruiken. Gebruik bijvoorbeeld geen onbewerkte string waarden uit een gebruikersinterface als onderdeel van een cachesleutel. Dergelijke sleutels kunnen schadelijke toegangspogingen toestaan of kunnen worden gebruikt bij een denial-of-service-aanval door uw cache te verzadiging met gegevens die betekenisloze sleutels hebben die zijn gegenereerd op basis van willekeurige tekenreeksen. In de voorgaande geldige voorbeelden zijn de volgorde gegevens en gebruikersvoorkeur gegevens duidelijk verschillend:

  • orderid en userId zijn intern gegenereerde id's.
  • region kan een opsomming of tekenreeks zijn uit een vooraf gedefinieerde lijst met bekende regio's.

Er is geen significantie geplaatst op tokens zoals / of _. De volledige sleutelwaarde wordt behandeld als een ondoorzichtige identificatietekenreeks. In dit geval kunt u de / en _ weglaten zonder dat de werking van de cache verandert, maar meestal wordt een scheidingsteken gebruikt om dubbelzinnigheid te voorkomen - bijvoorbeeld kan $"order{customerId}{orderId}" verwarring veroorzaken tussen:

  • customerId 42 met orderId 123
  • customerId 421 met orderId 23

(beide zouden de cachesleutel order42123genereren)

Deze richtlijnen zijn evenzeer van toepassing op elke string-gebaseerde cache-API, zoals HybridCache, IDistributedCacheen IMemoryCache.

U ziet dat de inline geïnterpoleerde tekenreekssyntaxis ($"..." in de voorgaande voorbeelden van geldige sleutels) zich rechtstreeks in de GetOrCreateAsync aanroep bevindt. Deze syntaxis wordt aanbevolen bij het gebruik van HybridCache, omdat geplande toekomstige verbeteringen mogelijk zijn die de noodzaak om een string toe te wijzen voor de sleutel in veel scenario's overslaan.

Aanvullende belangrijke overwegingen

  • Sleutels kunnen worden beperkt tot geldige maximumlengten. De standaard-HybridCache-implementatie (via AddHybridCache(...)) beperkt bijvoorbeeld sleutels standaard tot 1024 tekens. Dat getal kan worden geconfigureerd via HybridCacheOptions.MaximumKeyLength, waarbij langere sleutels de cachemechanismen omzeilen om verzadiging te voorkomen.
  • Sleutels moeten geldige Unicode-reeksen zijn. Als er ongeldige Unicode-reeksen worden doorgegeven, is het gedrag niet gedefinieerd.
  • Wanneer u een niet-verwerkte secundaire cache gebruikt, zoals IDistributedCache, kan de specifieke back-end-implementatie aanvullende beperkingen opleggen. Als hypothetisch voorbeeld kan een bepaalde back-end hoofdletterongevoelige sleutellogica gebruiken. De standaard-HybridCache (via AddHybridCache(...)) detecteert dit scenario om verwarringsaanvallen te voorkomen, maar dit kan er nog steeds toe leiden dat conflicterende sleutels sneller worden overschreven of verwijderd dan verwacht.

De alternatieve GetOrCreateAsync overbelasting

De alternatieve overbelading kan enige overhead verminderen bij de vastgelegde variabelen en callbacks per instantie, maar gaat ten koste van complexere code. Voor de meeste scenario's weegt de prestatieverhoging niet op tegen de complexiteit van de code. Hier volgt een voorbeeld waarin gebruik wordt gemaakt van de alternatieve overbelasting:

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

De methode SetAsync

In veel scenario's is GetOrCreateAsync de enige API die nodig is. Maar HybridCache heeft ook SetAsync om een object in de cache op te slaan zonder het eerst op te halen.

Cachevermeldingen per sleutel verwijderen

Wanneer de onderliggende gegevens voor een cachevermelding worden gewijzigd voordat deze verloopt, verwijdert u de vermelding expliciet door RemoveAsync aan te roepen met de sleutel voor de vermelding. Met een overload kunt u een verzameling sleutelwaarden opgeven.

Wanneer een vermelding wordt verwijderd, wordt deze verwijderd uit zowel de primaire als de secundaire cache.

Cachevermeldingen per tag verwijderen

Belangrijk

Deze functie is nog in ontwikkeling. Als u items per tag probeert te verwijderen, zult u merken dat deze geen effect heeft.

Tags kunnen worden gebruikt om cachevermeldingen te groeperen en ze samen ongeldig te maken.

Stel tags in bij het aanroepen van GetOrCreateAsync, zoals wordt weergegeven in het volgende voorbeeld:

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

Verwijder alle vermeldingen voor een opgegeven tag door RemoveByTagAsync aan te roepen met de tagwaarde. Met een overload kunt u een verzameling tag-waarden opgeven.

Wanneer een vermelding wordt verwijderd, wordt deze verwijderd uit zowel de primaire als de secundaire cache.

Opties

De methode AddHybridCache kan worden gebruikt om globale standaardinstellingen te configureren. In het volgende voorbeeld ziet u hoe u enkele van de beschikbare opties configureert:

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

De methode GetOrCreateAsync kan ook een HybridCacheEntryOptions object gebruiken om de algemene standaardwaarden voor een specifieke cachevermelding te overschrijven. Hier volgt een voorbeeld:

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

Zie de broncode voor meer informatie over de opties:

Grenzen

Met de volgende eigenschappen van HybridCacheOptions kunt u limieten configureren die van toepassing zijn op alle cachevermeldingen:

  • MaximumPayloadBytes - Maximale grootte van een cachevermelding. De standaardwaarde is 1 MB. Pogingen om waarden over deze grootte op te slaan, worden geregistreerd en de waarde wordt niet opgeslagen in de cache.
  • MaximumKeyLength - Maximale lengte van een cachesleutel. De standaardwaarde is 1024 tekens. Pogingen om waarden over deze grootte op te slaan, worden geregistreerd en de waarde wordt niet opgeslagen in de cache.

Serialisatie

Voor het gebruik van een secundaire out-of-process-cache is serialisatie vereist. Serialisatie wordt geconfigureerd als onderdeel van het registreren van de HybridCache-service. Typespecifieke en algemeen bruikbare serializers kunnen worden geconfigureerd via de AddSerializer en AddSerializerFactory methodes, aangeroepen vanuit de AddHybridCache-aanroep. De bibliotheek verwerkt standaard string en byte[] intern en gebruikt System.Text.Json voor alles. HybridCache kan ook andere serializers gebruiken, zoals protobuf of XML.

In het volgende voorbeeld wordt de service geconfigureerd voor het gebruik van een typespecifieke protobuf-serializer:

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

In het volgende voorbeeld wordt de service geconfigureerd voor het gebruik van een protobuf-serializer voor algemeen gebruik die veel protobuf-typen kan verwerken:

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

Voor de secundaire cache is een gegevensarchief vereist, zoals Redis of SqlServer. Als u Azure Cache voor Redis-wilt gebruiken, bijvoorbeeld:

  • Installeer het Microsoft.Extensions.Caching.StackExchangeRedis-pakket.

  • Maak een exemplaar van Azure Cache voor Redis.

  • Haal een verbindingsreeks op die verbinding maakt met het Redis-exemplaar. Zoek de verbindingsreeks door Toegangssleutels weergeven te selecteren op de pagina Overzicht in de Azure-portal.

  • Sla de verbindingsreeks op in de configuratie van de app. Gebruik bijvoorbeeld een gebruikersgeheimbestand dat eruitziet als de volgende JSON, met de verbindingsreeks in de sectie ConnectionStrings. Vervang <the connection string> door de werkelijke verbindingsreeks:

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • Registreer in DI de IDistributedCache-implementatie die het Redis-pakket biedt. Hiervoor roept u AddStackExchangeRedisCacheaan en geeft u de verbindingsreeks door. Bijvoorbeeld:

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • De Implementatie van Redis IDistributedCache is nu beschikbaar in de DI-container van de app. HybridCache gebruikt deze als secundaire cache en maakt gebruik van de serialisatiefunctie die hiervoor is geconfigureerd.

Zie de voorbeeld-app HybridCache-serialisatievoor meer informatie.

Cacheopslag

Standaard gebruikt HybridCacheMemoryCache voor de primaire cacheopslag. Cachevermeldingen worden in het proces opgeslagen, dus elke server heeft een afzonderlijke cache die verloren gaat wanneer het serverproces opnieuw wordt opgestart. Voor secundaire out-of-process-opslag, zoals Redis of SQL Server, gebruikt HybridCachede geconfigureerde IDistributedCache implementatie, indien van toepassing. Maar zelfs zonder een IDistributedCache-implementatie biedt de HybridCache-service nog steeds plaatsgebonden caching en stampede-bescherming.

Notitie

Bij het ongeldig maken van cachevermeldingen per sleutel of tags, worden deze ongeldig gemaakt op de huidige server en in de secundaire out-of-process-opslag. De cache in het geheugen op andere servers wordt echter niet beïnvloed.

Prestaties optimaliseren

Als u de prestaties wilt optimaliseren, configureert u HybridCache voor het hergebruik van objecten en voorkomt u byte[] toewijzingen.

Objecten opnieuw gebruiken

Door exemplaren opnieuw te gebruiken, kan HybridCache de overhead van CPU- en objecttoewijzingen verminderen die verbonden zijn met deserialisatie per aanroep. Dit kan leiden tot prestatieverbeteringen in scenario's waarbij de objecten in de cache groot zijn of regelmatig worden geopend.

In typische bestaande code die gebruikmaakt van IDistributedCache, resulteert elk ophalen van een object uit de cache in de deserialisatie. Dit gedrag betekent dat elke gelijktijdige aanroeper een afzonderlijk exemplaar van het object krijgt, dat niet kan communiceren met andere exemplaren. Het resultaat is threadveiligheid, omdat er geen risico is op gelijktijdige wijzigingen in dezelfde objectinstanties.

Omdat veel HybridCache gebruik wordt aangepast aan bestaande IDistributedCache code, behoudt HybridCache dit gedrag standaard om gelijktijdigheidsfouten te voorkomen. Objecten zijn echter inherent thread-safe als:

  • Ze zijn onveranderbare typen.
  • De code wijzigt deze niet.

In dergelijke gevallen informeert u HybridCache dat het veilig is om exemplaren opnieuw te gebruiken door:

  • Het type markeren als sealed. Het sealed trefwoord in C# betekent dat de klasse niet kan worden overgenomen.
  • Het [ImmutableObject(true)]-kenmerk toepassen op het type. Het kenmerk [ImmutableObject(true)] geeft aan dat de status van het object niet kan worden gewijzigd nadat het is gemaakt.

Vermijd byte[]-toewijzingen

HybridCache biedt ook optionele API's voor IDistributedCache implementaties om byte[] toewijzingen te voorkomen. Deze functie wordt geïmplementeerd door de preview-versies van de Microsoft.Extensions.Caching.StackExchangeRedis- en Microsoft.Extensions.Caching.SqlServer-pakketten. Zie IBufferDistributedCache Hier vindt u de .NET CLI-opdrachten voor het installeren van de pakketten voor meer informatie:

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

Aangepaste HybridCache-implementaties

Een concrete implementatie van de HybridCache abstracte klasse wordt opgenomen in het gedeelde framework en wordt geleverd via afhankelijkheidsinjectie. Ontwikkelaars zijn echter welkom om aangepaste implementaties van de API te bieden.

Compatibiliteit

De HybridCache-bibliotheek ondersteunt oudere .NET-runtimes, tot .NET Framework 4.7.2 en .NET Standard 2.0.

Aanvullende informatiebronnen

Zie de volgende bronnen voor meer informatie over HybridCache: