HybridCache-Bibliothek in ASP.NET Core

Wichtig

HybridCacheist derzeit noch in der Vorschau, wird aber vollständig nach .NET 9.0 in einer zukünftigen Nebenversion von .NET-Erweiterungen veröffentlicht.

In diesem Artikel wird erläutert, wie Sie die HybridCache-Bibliothek in einer ASP.NET Core-App konfigurieren und verwenden. Eine Einführung in die Bibliothek finden Sie im HybridCache-Abschnitt zur Übersicht über das Zwischenspeichern.

Abrufen der Bibliothek

Installieren Sie das Microsoft.Extensions.Caching.Hybrid-Paket.

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

Registrieren des Diensts

Fügen Sie den HybridCache-Dienst zum Container Abhängigkeitsinjektion (DI) hinzu, indem Sie AddHybridCache aufrufen:

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

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

builder.Services.AddHybridCache();

Der voranstehende Code registriert den HybridCache-Dienst mit Standardoptionen. Die Registrierungs-API kann auch Optionen und Serialisierung konfigurieren.

Abrufen und Speichern von Cacheeinträgen

Der HybridCache-Dienst stellt eine GetOrCreateAsync-Methode mit zwei Überladungen bereit, wobei ein Schlüssel verwendet wird und:

  • Eine Factory-Methode.
  • Zustand und eine Factorymethode.

Die Methode verwendet den Schlüssel, um das Objekt aus dem primären Cache abzurufen. Wenn das Element nicht im primären Cache gefunden wird (ein Cachefehler), überprüft es den sekundären Cache, falls einer konfiguriert ist. Wenn die Daten dort nicht gefunden werden (ein anderer Cachefehler), ruft es die Factorymethode auf, um das Objekt aus der Datenquelle abzurufen. Anschließend wird das Objekt sowohl im primären als auch im sekundären Cache gespeichert. Die Factorymethode wird nie aufgerufen, wenn das Objekt im primären oder sekundären Cache gefunden wird (ein Cachetreffer).

Der HybridCache-Dienst stellt sicher, dass nur ein gleichzeitiger Aufrufer für einen bestimmten Schlüssel die Factorymethode aufruft, und alle anderen Aufrufer warten auf das Ergebnis dieses Aufrufs. Der Wert CancellationToken übergeben an GetOrCreateAsync stellt den kombinierten Abbruch aller gleichzeitigen Anrufer dar.

Die Hauptüberladung GetOrCreateAsync

Die zustandslose Überladung GetOrCreateAsync wird für die meisten Szenarien empfohlen. Der Code zum Aufrufen ist relativ einfach. Ein Beispiel:

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

Die alternative GetOrCreateAsync-Überladung

Die alternative Überladung kann den Mehraufwand von erfassten Variablen und Rückrufen pro Instanz verringern, aber auf Kosten von komplexerem Code. In den meisten Szenarien überwiegt die Leistungssteigerung nicht die Codekomplexität. Hier ist ein Beispiel, das die alternative Überladung verwendet:

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

Die SetAsync -Methode

In vielen Szenarien ist GetOrCreateAsync die einzige erforderliche API. Aber HybridCache hat auch SetAsync, um ein Objekt im Cache zu speichern, ohne es zuerst abzurufen.

Entfernen von Cacheeinträgen nach Schlüssel

Wenn die zugrunde liegenden Daten für einen Cacheeintrag vor ablaufen, entfernen Sie den Eintrag explizit, indem Sie RemoveAsync mit dem Schlüssel zum Eintrag aufrufen. Mit einer Überladung können Sie eine Sammlung von Schlüsselwerten angeben.

Wenn ein Eintrag entfernt wird, wird er aus den primären und sekundären Caches entfernt.

Entfernen von Cacheeinträgen nach Tag

Tags können verwendet werden, um Cacheeinträge zu gruppieren und zusammen ungültig zu machen.

Legen Sie Tags beim Aufrufen von GetOrCreateAsync fest, wie im folgenden Beispiel gezeigt:

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

Entfernen Sie alle Einträge für ein angegebenes Tag, indem Sie RemoveByTagAsync mit dem Tagwert aufrufen. Mit einer Überladung können Sie eine Sammlung von Tagwerten angeben.

Wenn ein Eintrag entfernt wird, wird er aus den primären und sekundären Caches entfernt.

Optionen

Die AddHybridCache-Methode kann verwendet werden, um globale Standardwerte zu konfigurieren. Das folgende Beispiel zeigt, wie Sie einige der verfügbaren Optionen konfigurieren:

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

Die GetOrCreateAsync-Methode kann auch ein HybridCacheEntryOptions-Objekt verwenden, um die globalen Standardwerte für einen bestimmten Cacheeintrag außer Kraft zu setzen. Ein Beispiel:

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

Weitere Informationen zu den Optionen finden Sie im Quellcode:

Grenzwerte

Mit den folgenden Eigenschaften von HybridCacheOptions können Sie Grenzwerte konfigurieren, die für alle Cacheeinträge gelten:

  • MaximumPayloadBytes – Maximale Größe eines Cacheeintrags. Der Standardwert ist 1 MB. Versuche, Werte über dieser Größe zu speichern, werden protokolliert, und der Wert wird nicht im Cache gespeichert.
  • MaximumKeyLength – Maximale Länge eines Cacheschlüssels. Der Standardwert ist 1024 Zeichen. Versuche, Werte über dieser Größe zu speichern, werden protokolliert, und der Wert wird nicht im Cache gespeichert.

Serialisierung

Die Verwendung eines sekundären Out-of-Process-Caches erfordert eine Serialisierung. Die Serialisierung wird im Rahmen der Registrierung des HybridCache-Diensts konfiguriert. Typspezifische und allgemeine Serialisierer können über die Methoden AddSerializer und AddSerializerFactory konfiguriert werden, die vom AddHybridCache-Aufruf verkettet werden. Standardmäßig verarbeitet die Bibliothek string und byte[] intern und verwendet System.Text.Json für alles andere. HybridCache kann auch andere Serialisierer verwenden, z. B. Protobuf oder XML.

Im folgenden Beispiel wird der Dienst so konfiguriert, dass ein typspezifischer Protobuf-Serialisierer verwendet wird:

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

Im folgenden Beispiel wird der Dienst so konfiguriert, dass ein allgemeiner Protobuf-Serialisierer verwendet wird, der viele Protobuf-Typen verarbeiten kann:

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

Der sekundäre Cache erfordert einen Datenspeicher, z. B. Redis oder SqlServer. So verwenden Sie beispielsweise Azure Cache for Redis:

  • Installieren Sie das Microsoft.Extensions.Caching.StackExchangeRedis-Paket.

  • Erstellen Sie eine Instanz von Azure Cache for Redis.

  • Rufen Sie eine Verbindungszeichenfolge ab, die eine Verbindung mit der Redis-Instanz herstellt. Suchen Sie die Verbindungszeichenfolge, indem Sie Zugriffsschlüssel anzeigen auf der Seite Übersicht im Azure-Portal auswählen.

  • Speichern Sie die Verbindungszeichenfolge in der App-Konfiguration. Verwenden Sie z. B. eine Datei mit Benutzergeheimnissen, die wie die folgende JSON aussieht, mit der Verbindungszeichenfolge im Abschnitt ConnectionStrings. Ersetzen Sie <the connection string> durch die tatsächliche Verbindungszeichenfolge:

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • Registrieren Sie in DI die IDistributedCache-Implementierung, die durch das Redis-Paket bereitstellt wird. Rufen Sie dazu AddStackExchangeRedisCache auf, und übergeben Sie die Verbindungszeichenfolge. Zum Beispiel:

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • Die Redis-Implementierung IDistributedCache ist jetzt im DI-Container der App verfügbar. HybridCache verwendet sie als sekundären Cache und verwendet den dafür konfigurierten Serialisierer.

Weitere Informationen finden Sie in der Beispiel-App für die HybridCache-Serialisierung.

Cachespeicher

Standardmäßig verwendet HybridCache MemoryCache für den primären Cachespeicher. Zwischengespeicherte Einträge werden „In-Process“ gespeichert, sodass jeder Server über einen separaten Cache verfügt, der bei jedem Neustart des Serverprozesses verloren geht. Für sekundären Out-of-Process-Speicher, z. B. Redis oder SQL Server, verwendet HybridCache ggf. die konfigurierte IDistributedCache-Implementierung. Aber auch ohne IDistributedCacheImplementierung bietet der HybridCache-Dienst weiterhin In-Process-Cache- und Stampede-Schutz.

Optimieren der Leistung

Um die Leistung zu optimieren, konfigurieren Sie die Wiederverwendung von HybridCache-Objekten und vermeiden Sie byte[]-Zuordnungen.

Wiederverwenden von Objekten

Durch die erneute Verwendung von Instanzen kann HybridCache den Mehraufwand der CPU- und Objektzuordnungen verringern, die mit der Deserialisierung pro Aufruf verbunden sind. Dies kann zu Leistungsverbesserungen in Szenarien führen, in denen die zwischengespeicherten Objekte groß sind oder häufig aufgerufen werden.

Im typischen vorhandenen Code, der IDistributedCache verwendet, führt jeder Abruf eines Objekts aus dem Cache zur Deserialisierung. Dieses Verhalten bedeutet, dass jeder gleichzeitige Aufrufer eine separate Instanz des Objekts erhält, die nicht mit anderen Instanzen interagieren kann. Das Ergebnis ist Threadsicherheit, da es kein Risiko für gleichzeitige Änderungen an derselben Objektinstanz gibt.

Da viele HybridCache-Verwendungen von vorhandenem IDistributedCache-Code angepasst werden, behält HybridCache dieses Verhalten standardmäßig bei, um die Einführung von Parallelitätsfehlern zu vermeiden. Objekte sind jedoch inhärent threadsicher, wenn:

  • Sie unveränderliche Typen sind.
  • Der Code sie nicht ändert.

Informieren Sie in solchen Fällen HybridCache, dass es sicher ist, Instanzen wiederzuverwenden, indem Sie:

  • Den Typ als sealed markieren. Das Schlüsselwort sealed in C# bedeutet, dass die Klasse nicht geerbt werden kann.
  • Anwenden des [ImmutableObject(true)]-Attributs auf den Typ. Das Attribut [ImmutableObject(true)] gibt an, dass der Status des Objekts nach der Erstellung nicht mehr geändert werden kann.

Vermeiden von byte[]-Zuordnungen

HybridCache stellt auch optionale APIs für IDistributedCache-Implementierungen bereit, um byte[]-Zuordnungen zu vermeiden. Dieses Feature wird von den Vorschauversionen der Microsoft.Extensions.Caching.StackExchangeRedis- und Microsoft.Extensions.Caching.SqlServer-Pakete implementiert. Weitere Informationen finden Sie unter IBufferDistributedCache Hier finden Sie die .NET CLI-Befehle zum Installieren der Pakete:

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

Benutzerdefinierte HybridCache-Implementierungen

Eine konkrete Implementierung der abstrakten Klasse HybridCache ist im freigegebenen Framework enthalten und wird über Abhängigkeitsinjektion bereitgestellt. Entwickler können jedoch benutzerdefinierte Implementierungen der API bereitstellen.

Kompatibilität

Die HybridCache-Bibliothek unterstützt ältere .NET-Runtimes bis zu .NET Framework 4.7.2 und .NET Standard 2.0.

Zusätzliche Ressourcen

Weitere Informationen zu HybridCache finden Sie in den folgenden Ressourcen: