次の方法で共有


ASP.NET Core での HybridCache ライブラリ

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

この記事では、ASP.NET Core アプリで HybridCache ライブラリを構成して使う方法について説明します。 このライブラリの概要については、キャッシュの概要についての記事の HybridCache に関するセクションをご覧ください。

ライブラリの入手

Microsoft.Extensions.Caching.Hybrid パッケージをインストールします。

dotnet add package Microsoft.Extensions.Caching.Hybrid --prerelease

サービスを登録する

AddHybridCache を呼び出して、HybridCache サービスを依存関係の挿入 (DI) コンテナーに追加します。

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

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

builder.Services.AddHybridCache();

上記のコードは、HybridCache サービスを既定のオプションで登録します。 登録 API では、オプションシリアル化を構成することもできます。

キャッシュ エントリを取得して格納する

HybridCache サービスで提供される GetOrCreateAsync メソッドには、キーと次のものを受け取る 2 つのオーバーロードがあります。

  • ファクトリ メソッド。
  • 状態とファクトリ メソッド。

このメソッドは、キーを使って、プライマリ キャッシュからオブジェクトの取得を試みます。 項目がプライマリ キャッシュに見つからない場合 (キャッシュ ミス)、セカンダリ キャッシュでそれが構成されているかどうか確認されます。 そこにデータが見つからない場合 (別のキャッシュ ミス)、ファクトリ メソッドを呼び出してデータ ソースからオブジェクトを取得します。 その後、プライマリとセカンダリ両方のキャッシュにオブジェクトを格納します。 プライマリまたはセカンダリどちらかのキャッシュでオブジェクトが見つかった場合 (キャッシュ ヒット)、ファクトリ メソッドは呼び出されません。

HybridCache サービスは、特定のキーに対して同時に複数の呼び出し元がある場合、そのうちの 1 つだけがファクトリ メソッドを実行し、他のすべての呼び出し元はその実行の結果を待機することを保証します。GetOrCreateAsync に渡される CancellationToken は、すべての同時呼び出し元の結合されたキャンセルを表します。

メインの GetOrCreateAsync オーバーロード

GetOrCreateAsync のステートレス オーバーロードは、ほとんどのシナリオに推奨されます。 それを呼び出すコードは比較的単純です。 次に例を示します。

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),
            token: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

代替の GetOrCreateAsync オーバーロード

代替オーバーロードを使うと、キャプチャした変数とインスタンスごとのコールバックによるオーバーヘッドが軽減される可能性がありますが、代わりにコードは複雑になります。 ほとんどのシナリオでは、コードの複雑さを上回るほどパフォーマンスは向上しません。 代替オーバーロードを使用する例を次に示します。

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),
            token: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

SetAsync メソッド

多くのシナリオでは、必要な API は GetOrCreateAsync のみです。 ただし、HybridCache には、最初に取得を試みずにオブジェクトをキャッシュに格納する SetAsync もあります。

期限が切れていないキャッシュ エントリを削除する

キャッシュ エントリが期限切れになる前に、その基になっているデータが変更された場合は、エントリを明示的に削除できます。 削除するエントリはキーで指定できます。 エントリが削除されると、プライマリとセカンダリ両方のキャッシュから削除されます。

キーを使用して削除する

次のメソッドは、キーによるキャッシュ エントリの削除をサポートしています。

  • RemoveKeyAsync
  • RemoveKeysAsync

注: 将来的に、これらは RemoveByKeyAsyncRemoveByKeysAsync に変更されます。

[オプション]

AddHybridCache メソッドを使って、グローバルな既定値を構成できます。 次の例では、使用可能なオプションの一部を構成する方法を示します。

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

GetOrCreateAsync メソッドは、HybridCacheEntryOptions オブジェクトを受け取って、特定のキャッシュ エントリのグローバル既定値をオーバーライドすることもできます。 次に例を示します。

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,
            token: token
        );
    }
    
    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

オプションについて詳しくは、ソース コードを参照してください。

制限

HybridCacheOptions の次のプロパティを使うと、すべてのキャッシュ エントリに適用される制限を構成できます。

  • MaximumPayloadBytes: キャッシュ エントリの最大サイズ。 既定値は 1 MB です。 このサイズを超える値を格納しようとするとログされ、値はキャッシュに格納されません。
  • MaximumKeyLength: キャッシュ キーの最大長。 既定値は 1,024 文字です。 このサイズを超える値を格納しようとするとログされ、値はキャッシュに格納されません。

シリアル化

セカンダリのアウトプロセス キャッシュを使用するには、シリアル化が必要です。 シリアル化は、HybridCache サービスの登録の一環として構成されます。 AddHybridCache 呼び出しからチェーンされた WithSerializer および WithSerializerFactory メソッドを介して、型固有シリアライザーと汎用シリアライザーを構成できます。 既定では、このライブラリは stringbyte[] を内部的に処理し、その他すべてに System.Text.Json を使用します。 HybridCache は、protobuf や XML など、他の種類のシリアライザーを使用することもできます。

次の例では、型固有の 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)
        };
    }).WithSerializer<SomeProtobufMessage, 
        GoogleProtobufSerializer<SomeProtobufMessage>>();

次の例では、多くの protobuf 型を処理できる汎用 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)
    };
}).WithSerializerFactory<GoogleProtobufSerializerFactory>();

セカンダリ キャッシュには、Redis や SqlServer などのデータ ストアが必要です。 たとえば、Azure Cache for Redis を使用するには次の操作を行います。

  • Microsoft.Extensions.Caching.StackExchangeRedis パッケージをインストールします。

  • Azure Cache for Redis のインスタンスを作成します。

  • Redis インスタンスに接続するための接続文字列を取得します。 Azure portal の [概要] ページで [アクセス キーの表示] を選択して、接続文字列を見つけます。

  • 接続文字列をアプリの構成に格納します。 たとえば、ConnectionStrings セクションに接続文字列を含む次の JSON のような ユーザー シークレット ファイルを使用します。 <the connection string> を実際の接続文字列に置き換えます。

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • DI に、Redis パッケージで提供される IDistributedCache 実装を登録します。 そのためには、AddStackExchangeRedisCache を呼び出し、接続文字列を渡します。 次に例を示します。

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • これで、Redis IDistributedCache 実装をアプリの DI コンテナーから使用できるようになりました。 HybridCache は、その実装をセカンダリ キャッシュとして使用し、それ用に構成されたシリアライザーを使用します。

詳細については、HybridCache シリアル化サンプル アプリを参照してください。

キャッシュ ストレージ

既定では、HybridCache はプライマリ キャッシュ ストレージに MemoryCache を使います。 キャッシュ エントリはプロセス内で保存されるため、サーバーごとに、サーバー プロセスが再起動されるたびに失われる個別のキャッシュがあります。 Redis や SQL Server などのセカンダリ プロセス外ストレージの場合、HybridCache は、IDistributedCache の実装が構成されている場合はそれを使います。 ただし、IDistributedCache の実装がなくても、HybridCache サービスはプロセス内キャッシュとスタンピード保護を提供します。

パフォーマンスを最適化する

パフォーマンスを最適化するには、オブジェクトを再利用し、byte[] の割り当てが行われないように、HybridCache を構成します。

オブジェクトを再利用する

IDistributedCache を使用する一般的な既存のコードでは、キャッシュからオブジェクトを取得するたびに、逆シリアル化が行われます。 この動作は、各同時実行呼び出し元が、その他のインスタンスとは対話できない、オブジェクトの個別のインスタンスを取得することを意味します。 この結果、同じオブジェクト インスタンスに同時に変更を加えるリスクがなくなるため、スレッド セーフになります。

HybridCache の使用の多くは既存の IDistributedCache コードから適応されるため、HybridCache は、コンカレンシーのバグが発生しないように既定でこの動作を保持します。 ただし、次の場合、オブジェクトは本質的にスレッドセーフです。

  • 不変型である。
  • コードでそれを変更しない。

このような場合、次の方法により、インスタンスを再利用しても安全であることを HybridCache に通知します。

  • 型を sealed としてマークします。 C# の sealed キーワードは、クラスを継承できないことを意味します。
  • その型に [ImmutableObject(true)] 属性を追加します。 [ImmutableObject(true)] 属性は、オブジェクトの作成後にオブジェクトの状態を変更できないことを示します。

インスタンスを再利用することで、HybridCache は、呼び出しごとの逆シリアル化に関連する CPU とオブジェクトの割り当てのオーバーヘッドを削減できます。 この結果、キャッシュされたオブジェクトが大きいか頻繁にアクセスされるシナリオでパフォーマンスが向上する可能性があります。

byte[] の割り当てを行わない

HybridCache には、byte[] の割り当てを回避するため、IDistributedCache の実装に対するオプションの API も用意されています。 この機能は、Microsoft.Extensions.Caching.StackExchangeRedis および Microsoft.Extensions.Caching.SqlServer パッケージのプレビュー バージョンによって実装されます。 詳細については、IBufferDistributedCache を参照してください。パッケージをインストールする .NET CLI コマンドを次に示します。

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

カスタム HybridCache の実装

HybridCache 抽象クラスの具象実装は、共有フレームワークに含まれており、依存関係の挿入によって提供されます。 ただし、開発者は API のカスタム実装を提供してかまいません。

互換性

このライブラリは、.NET Framework 4.7.2 と .NET Standard 2.0 までの以前の .NET ランタイムをサポートしています。

その他のリソース

HybridCache について詳しくは、次のリソースをご覧ください。