Bagikan melalui


Pustaka HybridCache di ASP.NET Core

Artikel ini menjelaskan cara mengonfigurasi dan menggunakan HybridCache pustaka di aplikasi ASP.NET Core. Untuk pengenalan pustaka, lihat bagian HybridCache ikhtisar Penembolokan.

Dapatkan perpustakaan

Pasang paket Microsoft.Extensions.Caching.Hybrid tersebut.

dotnet add package Microsoft.Extensions.Caching.Hybrid

Mendaftarkan layanan

HybridCache Tambahkan layanan ke kontainer injeksi dependensi (DI) dengan memanggil AddHybridCache:

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

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

builder.Services.AddHybridCache();

Kode sebelumnya mendaftarkan HybridCache layanan dengan opsi default. API pendaftaran juga dapat mengonfigurasi opsi dan serialisasi.

Mendapatkan dan menyimpan entri cache

Layanan HybridCache menyediakan GetOrCreateAsync metode dengan dua overload, yang menerima sebuah kunci dan:

  • Metode pabrikasi
  • Negara, dan metode pabrikasi.

Metode ini menggunakan kunci untuk mencoba mengambil objek dari cache utama. Jika item tidak ditemukan di cache utama (cache tidak ditemukan), sistem kemudian memeriksa cache sekunder jika dikonfigurasi. Jika tidak menemukan data di sana (cache miss lain), ia memanggil metode factory untuk mendapatkan objek dari sumber data. Kemudian menyimpan objek di cache primer dan sekunder. Metode factory tidak pernah dipanggil jika objek ditemukan di cache primer atau sekunder (cache hit).

Layanan HybridCache ini memastikan bahwa hanya satu penelepon yang dapat secara bersamaan memanggil metode factory untuk kunci tertentu, sementara semua penelepon lainnya menunggu hasil dari panggilan tersebut. Yang CancellationToken diteruskan ke GetOrCreateAsync mewakili pembatalan gabungan semua pemanggil yang bersamaan.

Kelebihan beban utama GetOrCreateAsync

Penggunaan berlebihan GetOrCreateAsync tanpa status direkomendasikan untuk sebagian besar skenario. Kode untuk menyebutnya relatif sederhana. Berikut contohnya:

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

Panduan kunci cache

key yang diteruskan ke GetOrCreateAsync harus mengidentifikasi data yang di-cache secara unik:

  • Dalam hal nilai pengidentifikasi yang digunakan untuk mengambil data tersebut dari sumbernya.
  • Dalam hal data lain yang di-cache dalam aplikasi.

Kedua jenis keunikan biasanya dipastikan dengan menggunakan perangkaian string untuk membuat satu string kunci yang terdiri dari berbagai bagian yang digabungkan menjadi satu string. Contohnya:

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

Atau

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

Ini adalah tanggung jawab pemanggil untuk memastikan bahwa skema kunci valid dan tidak dapat menyebabkan data menjadi bingung.

Hindari menggunakan input pengguna eksternal langsung dalam kunci cache. Misalnya, jangan gunakan string mentah dari antarmuka pengguna sebagai kunci cache. Melakukannya dapat mengekspos aplikasi Anda terhadap risiko keamanan, seperti akses yang tidak sah atau serangan penolakan layanan yang disebabkan oleh membanjiri cache dengan kunci acak atau tidak berarti. Dalam contoh valid sebelumnya, data urutan dan preferensi pengguna dipisahkan dengan jelas dan menggunakan pengidentifikasi tepercaya:

  • orderid dan userId adalah pengidentifikasi yang dihasilkan secara internal.
  • region mungkin enum atau string dari daftar wilayah yang diketahui yang telah ditentukan sebelumnya.

Tidak ada signifikansi yang ditempatkan pada token seperti / atau _. Seluruh nilai kunci diperlakukan sebagai string identifikasi buram. Dalam hal ini, Anda dapat menghilangkan / dan _ tanpa perubahan pada cara cache berfungsi, tetapi pemisah biasanya digunakan untuk menghindari ambiguitas - misalnya $"order{customerId}{orderId}" dapat menyebabkan kebingungan antara:

  • customerId 42 dengan orderId 123
  • customerId 421 dengan orderId 23

Kedua contoh sebelumnya akan menghasilkan kunci order42123cache .

Panduan ini berlaku sama untuk API cache berbasis stringapa pun, seperti HybridCache, IDistributedCache, dan IMemoryCache.

Perhatikan bahwa sintaks string terinterpolasi sebaris ($"..." dalam contoh kunci yang valid sebelumnya) berada tepat di dalam panggilan GetOrCreateAsync. Sintaks ini direkomendasikan ketika menggunakan HybridCache, karena memungkinkan peningkatan yang direncanakan di masa depan tanpa perlu mengalokasikan string untuk kunci dalam banyak skenario.

Pertimbangan kunci tambahan

  • Kunci dapat dibatasi untuk panjang maksimum yang valid. Misalnya, implementasi HybridCache default (melalui AddHybridCache(...)) membatasi kunci hingga 1024 karakter secara default. Nomor tersebut dapat dikonfigurasi melalui HybridCacheOptions.MaximumKeyLength, dengan kunci yang lebih panjang mengabaikan mekanisme cache untuk mencegah kejenuhan.
  • Kunci harus berupa urutan Unicode yang valid. Jika urutan Unicode yang tidak valid diteruskan, maka perilakunya menjadi tidak terdefinisi.
  • Saat menggunakan cache sekunder di luar proses seperti IDistributedCache, implementasi backend dapat memberlakukan pembatasan tambahan. Sebagai contoh hipotetis, backend tertentu mungkin menggunakan logika kunci yang tidak peka terhadap huruf besar-kecil. Default HybridCache (melalui AddHybridCache(...)) mendeteksi skenario ini untuk mencegah serangan kebingungan atau serangan alias (menggunakan kesetaraan string bitwise). Namun, skenario ini mungkin masih mengakibatkan kunci yang bertentangan ditimpa atau dihapus lebih cepat daripada yang diharapkan.

Kelebihan beban alternatif GetOrCreateAsync

Overload alternatif dapat mengurangi sebagian overhead dari variabel yang ditangkap dan panggilan balik per instans, tetapi dengan mengorbankan kode yang lebih kompleks. Untuk sebagian besar skenario, peningkatan performa tidak melebihi kompleksitas kode. Berikut adalah contoh yang menggunakan kelebihan beban alternatif:

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

Metode SetAsync

Dalam banyak skenario, GetOrCreateAsync adalah satu-satunya API yang diperlukan. Tetapi HybridCache juga harus SetAsync menyimpan objek di cache tanpa mencoba mengambilnya terlebih dahulu.

Hapus entri cache menurut kunci

Ketika data yang mendasar untuk entri cache berubah sebelum kedaluwarsa, hapus entri secara eksplisit dengan memanggil RemoveAsync dengan kunci ke entri. Kelebihan beban memungkinkan Anda menentukan kumpulan nilai kunci.

Ketika entri dihapus, entri dihapus dari cache primer dan sekunder.

Menghapus entri cache menurut tag

Tag dapat digunakan untuk mengelompokkan entri cache dan membatalkannya bersama-sama.

Atur tag saat memanggil GetOrCreateAsync, seperti yang ditunjukkan dalam contoh berikut:

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

Hapus semua entri untuk tag tertentu dengan memanggil RemoveByTagAsync dengan nilai tag. Kelebihan beban memungkinkan Anda menentukan kumpulan nilai tag.

Baik IMemoryCache maupun IDistributedCache tidak memiliki dukungan langsung untuk konsep tag, sehingga pembatalan berbasis tag hanya merupakan operasi logis . Ini tidak secara aktif menghapus nilai dari cache lokal atau terdistribusi. Sebaliknya, ia memastikan bahwa saat menerima data dengan tag tersebut, data diperlakukan sebagai cache-miss dari cache lokal dan jarak jauh. Nilai kedaluwarsa dari IMemoryCache dan IDistributedCache seperti biasanya, berdasarkan masa berlaku yang dikonfigurasi.

Menghapus semua entri cache

Tag tanda bintang (*) dicadangkan sebagai simbol pengganti dan tidak diizinkan untuk nilai individu. Memanggil RemoveByTagAsync("*") memiliki efek membuat semuaHybridCache data menjadi tidak valid, bahkan data yang tidak memiliki tag apa pun. Seperti halnya tag individu, ini adalah operasi logis , dan nilai individual terus ada sampai kedaluwarsa secara alami. Pencocokan pola glob tidak didukung. Misalnya, Anda tidak dapat menggunakan RemoveByTagAsync("foo*") untuk menghapus semuanya yang dimulai dengan foo.

Pertimbangan tag tambahan

  • Sistem tidak membatasi jumlah tag yang dapat Anda gunakan, tetapi serangkaian tag besar mungkin berdampak negatif pada performa.
  • Tag tidak boleh kosong, hanya spasi kosong, atau nilai *yang dipesan .

Opsi

Metode AddHybridCache ini dapat digunakan untuk mengonfigurasi default global. Contoh berikut menunjukkan cara mengonfigurasi beberapa opsi yang tersedia:

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

Metode GetOrCreateAsync juga dapat mengambil HybridCacheEntryOptions objek untuk menggantikan default global untuk entri cache tertentu. Berikut contohnya:

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

Untuk informasi selengkapnya tentang opsi, lihat kode sumber:

Batas

Properti HybridCacheOptions berikut memungkinkan Anda mengonfigurasi batasan yang berlaku untuk semua entri cache:

  • MaximumPayloadBytes - Ukuran maksimum entri cache. Nilai defaultnya adalah 1 MB. Upaya untuk menyimpan nilai dalam ukuran ini dicatat, dan nilainya tidak disimpan dalam cache.
  • MaximumKeyLength - Panjang maksimum kunci cache. Nilai defaultnya adalah 1024 karakter. Upaya untuk menyimpan nilai dalam ukuran ini dicatat, dan nilainya tidak disimpan dalam cache.

Serialisasi

Penggunaan cache sekunder dan di luar proses memerlukan serialisasi. Serialisasi dikonfigurasi sebagai bagian dari pendaftaran layanan HybridCache. Serializer khusus tipe dan umum dapat dikonfigurasi melalui metode AddSerializer dan AddSerializerFactory, yang ditautkan dari panggilan AddHybridCache. Secara default, pustaka menangani string dan byte[] secara internal, dan menggunakan System.Text.Json untuk yang lain. HybridCache juga dapat menggunakan serializer lain, seperti protobuf atau XML.

Contoh berikut mengonfigurasi layanan untuk menggunakan serializer protobuf yang spesifik untuk tipe tertentu:

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

Contoh berikut mengonfigurasi layanan untuk menggunakan serializer protobuf yang serbaguna, yang dapat menangani berbagai jenis 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>();

Cache sekunder memerlukan penyimpanan data, seperti Redis, SQL Server, atau Postgres. Untuk menggunakan Azure Cache for Redis, misalnya:

  • Pasang paket Microsoft.Extensions.Caching.StackExchangeRedis tersebut.

  • Buat instance dari Azure Cache untuk Redis.

  • Dapatkan string koneksi untuk instans Redis. Temukan string koneksi dengan memilih Tampilkan kunci akses di halaman Gambaran Umum di portal Azure.

  • Simpan string koneksi dalam konfigurasi aplikasi. Misalnya, gunakan file rahasia pengguna yang terlihat seperti JSON berikut, dengan string koneksi di bagian .ConnectionStrings Ganti <the connection string> dengan string koneksi aktual:

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • Daftarkan dalam DI IDistributedCache implementasi yang disediakan oleh paket Redis. Untuk melakukan itu, panggil AddStackExchangeRedisCache, dan berikan string koneksi. Contohnya:

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • Implementasi Redis IDistributedCache sekarang tersedia dari kontainer DI aplikasi. HybridCache menggunakannya sebagai cache sekunder dan menggunakan serializer yang dikonfigurasi untuk cache tersebut.

Untuk informasi selengkapnya, lihat aplikasi sampel serialisasi HybridCache.

Penyimpanan cache

Secara default HybridCache menggunakan MemoryCache untuk penyimpanan cache utamanya. Entri cache disimpan dalam proses, sehingga setiap server memiliki cache terpisah yang hilang setiap kali proses server dimulai ulang. Untuk penyimpanan di luar proses sekunder, seperti Redis, SQL Server, atau Postgres, HybridCache menggunakan implementasi yang dikonfigurasiIDistributedCache, jika ada. Tetapi bahkan tanpa implementasi IDistributedCache, layanan HybridCache masih menyediakan penyimpanan sementara dalam proses dan perlindungan terhadap serbuan .

Nota

Saat menghapus entri cache berdasarkan kunci atau tag, entri tersebut dianggap tidak berlaku di server saat ini dan di penyimpanan sekunder di luar proses. Namun, cache dalam memori di server lain tidak terpengaruh.

Mengoptimalkan performa

Untuk mengoptimalkan performa, konfigurasikan HybridCache untuk menggunakan kembali objek dan menghindari byte[] alokasi.

Gunakan kembali objek

Dengan menggunakan kembali instance, HybridCache dapat mengurangi overhead CPU dan alokasi objek yang terkait dengan proses deserialisasi setiap panggilan. Hal ini dapat menyebabkan peningkatan performa dalam skenario di mana objek yang di-cache besar atau sering diakses.

Dalam kode umum yang ada yang menggunakan IDistributedCache, setiap pengambilan objek dari cache menghasilkan deserialisasi. Perilaku ini berarti bahwa setiap pemanggil bersamaan mendapatkan instans terpisah dari objek, yang tidak dapat berinteraksi dengan instans lain. Hasilnya adalah keamanan utas (thread safety), karena tidak ada risiko modifikasi bersamaan terhadap objek yang sama.

Karena banyak penggunaan HybridCache akan diadaptasi dari kode IDistributedCache yang ada, HybridCache mempertahankan perilaku ini secara default untuk menghindari memperkenalkan bug konkurensi. Namun, objek secara inheren aman terhadap utas jika:

  • Jenisnya tidak dapat diubah.
  • Kode tidak mengubahnya.

Dalam kasus seperti itu, informasikan HybridCache bahwa aman untuk menggunakan kembali instans dengan membuat kedua perubahan berikut:

  • Menandai jenis sebagai sealed. Kata sealed kunci dalam C# berarti bahwa kelas tidak dapat diwariskan.
  • Menerapkan atribut [ImmutableObject(true)] pada jenis. Atribut [ImmutableObject(true)] menunjukkan bahwa status objek tidak dapat diubah setelah dibuat.

Hindari alokasi byte[]

HybridCache juga menyediakan API untuk implementasi IDistributedCache yang bersifat opsional, guna menghindari alokasi byte[]. Fitur ini diimplementasikan oleh versi pratinjau paket Microsoft.Extensions.Caching.StackExchangeRedis, , Microsoft.Extensions.Caching.SqlServerdan Microsoft.Extensions.Caching.Postgres . Untuk informasi selengkapnya, lihat IBufferDistributedCache . Berikut adalah perintah .NET CLI untuk menginstal paket:

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

Implementasi Khas HybridCache

Implementasi konkret dari HybridCache kelas abstrak disertakan dalam kerangka kerja bersama dan disediakan melalui injeksi dependensi. Tetapi pengembang dipersilakan untuk menyediakan atau menggunakan implementasi kustom API, misalnya FusionCache.

Menggunakan Cache Hibrid dengan AOT Asli

Pertimbangan khusus AOT Asli berikut berlaku untuk HybridCache:

  • Serialisasi

    AOT asli tidak mendukung serialisasi berbasis refleksi runtime. Jika Anda menyimpan jenis kustom, Anda harus menggunakan generator sumber atau secara eksplisit mengonfigurasi serializer yang kompatibel dengan AOT, seperti System.Text.Json pembuatan sumber. HybridCache masih dalam pengembangan, dan menyederhanakan cara menggunakannya dengan AOT adalah prioritas tinggi untuk pengembangan tersebut. Untuk informasi selengkapnya, lihat permintaan pull dotnet/extensions#6475

  • Pemangkasan

    Pastikan semua jenis cache Anda direferensikan dengan cara yang mencegahnya dipangkas oleh pengkompilasi AOT. Menggunakan generator sumber untuk serialisasi membantu persyaratan ini. Untuk informasi selengkapnya, lihat dukungan ASP.NET Core untuk AOT Asli.

Jika Anda menyiapkan serialisasi dan pemangkasan dengan benar, HybridCache akan berfungsi sama di Native AOT seperti di aplikasi ASP.NET Core biasa.

Kompatibilitas

Pustaka HybridCache mendukung runtime .NET yang lebih lama, hingga .NET Framework 4.7.2 dan .NET Standard 2.0.

Sumber Daya Tambahan:

Untuk informasi selengkapnya, lihat HybridCache kode sumber