Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ebben a cikkben megismerheti a különböző gyorsítótárazási mechanizmusokat. A gyorsítótárazás az adatok közbenső rétegben való tárolásának művelete, amely felgyorsítja a későbbi adatlekéréseket. A gyorsítótárazás elméletileg teljesítményoptimalizálási stratégia és tervezési szempont. A gyorsítótárazás jelentősen javíthatja az alkalmazások teljesítményét azáltal, hogy a ritkán változó (vagy költséges) adatok könnyebben elérhetővé válnak. Ez a cikk három gyorsítótárazási módszert mutat be, és mindegyikhez biztosít minta forráskódot:
- Microsoft.Extensions.Caching.Memory: Memóriabeli gyorsítótárazás egykiszolgálós forgatókönyvekhez
- Microsoft.Extensions.Caching.Hybrid: Hibrid gyorsítótárazás, amely a memóriában és az elosztott gyorsítótárazást további funkciókkal kombinálja
- Microsoft.Extensions.Caching.Distributed: Elosztott gyorsítótárazás többkiszolgálós forgatókönyvekhez
Fontos
A .NET-ben két MemoryCache osztály található, az egyik a System.Runtime.Caching névtérben, a másik a Microsoft.Extensions.Caching névtérben:
Bár ez a cikk a gyorsítótárazással foglalkozik, nem tartalmazza a System.Runtime.Caching NuGet-csomagot. Az összes MemoryCache hivatkozás a Microsoft.Extensions.Caching névtéren belül található.
Az összes csomag készen áll a Microsoft.Extensions.* függőséginjektálásra (DI). A IMemoryCache, HybridCacheés IDistributedCache interfészek szolgáltatásokként is használhatók.
Memóriabeli gyorsítótárazás
Ebben a szakaszban megismerheti a Microsoft.Extensions.Caching.Memory csomagot. A IMemoryCache jelenlegi implementációja egy burkoló ConcurrentDictionary<TKey,TValue> köré építve, funkciókban gazdag API-t biztosít. A gyorsítótárban lévő bejegyzéseket a ICacheEntry rendszer jelöli, és bármely object. A memóriabeli gyorsítótárazási megoldás kiválóan alkalmas olyan alkalmazásokhoz, amelyek egyetlen kiszolgálón futnak, ahol a gyorsítótárazott adatok memóriát bérelnek az alkalmazás folyamatában.
Jótanács
Többkiszolgálós gyorsítótárazási forgatókönyvek esetén az elosztott gyorsítótárazási módszert érdemes a memóriabeli gyorsítótárazás alternatívaként használni.
Memóriabeli gyorsítótárazási API
A gyorsítótár fogyasztója szabályozhatja a csúszó és abszolút lejáratot is.
- ICacheEntry.AbsoluteExpiration
- ICacheEntry.AbsoluteExpirationRelativeToNow
- ICacheEntry.SlidingExpiration
A lejárat beállítása miatt a gyorsítótár bejegyzései törlődnek , ha nem férnek hozzá a lejárati idő kiosztott időszakán belül. A felhasználók további lehetőségeket is használhatnak a gyorsítótárbejegyzések szabályozására.MemoryCacheEntryOptions Mindegyik ICacheEntry párosítva van a MemoryCacheEntryOptionslejárati kilakoltatás funkcióvalIChangeToken, a prioritási beállításokkal és a CacheItemPriorityvezérlővelICacheEntry.Size. A vonatkozó bővítménymetelyek a következők:
- MemoryCacheEntryExtensions.AddExpirationToken
- MemoryCacheEntryExtensions.RegisterPostEvictionCallback
- MemoryCacheEntryExtensions.SetSize
- MemoryCacheEntryExtensions.SetPriority
Példa a memóriabeli gyorsítótárra
Az alapértelmezett IMemoryCache implementáció használatához hívja meg a AddMemoryCache bővítménymetódust, hogy regisztrálja az összes szükséges szolgáltatást a DI-ben. A következő kódmintában az általános gazdagépet használják a DI-funkciók elérhetővé fogadására:
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
using IHost host = builder.Build();
A .NET-alapú számítási feladattól függően előfordulhat, hogy másként éri el a IMemoryCache, például konstruktorinjektálással. Ebben a példában a IServiceProvider példányt használja a host-en, és meghívja az általános GetRequiredService<T>(IServiceProvider) kiterjesztési metódust.
IMemoryCache cache =
host.Services.GetRequiredService<IMemoryCache>();
A memóriabeli gyorsítótárazási szolgáltatások regisztrálása és a DI-n keresztüli megoldása után készen áll a gyorsítótárazás megkezdésére. Ez a minta az angol ábécé "A"-tól "Z"-ig terjedő betűin iterál végig. A record AlphabetLetter típus tartalmazza a levélre mutató hivatkozást, és létrehoz egy üzenetet.
file record AlphabetLetter(char Letter)
{
internal string Message =>
$"The '{Letter}' character is the {Letter - 64} letter in the English alphabet.";
}
Jótanács
A file hozzáférés-módosító a típuson AlphabetLetter van használva, mivel az a Program.cs fájlon belül van definiálva, és csak a fájlból érhető el. További információ: fájl (C# referencia). A teljes forráskód megtekintéséhez tekintse meg a Program.cs szakaszt.
A minta tartalmaz egy segédfüggvényt, amely végigiterál az ábécé betűin.
static async ValueTask IterateAlphabetAsync(
Func<char, Task> asyncFunc)
{
for (char letter = 'A'; letter <= 'Z'; ++letter)
{
await asyncFunc(letter);
}
Console.WriteLine();
}
Az előző C# kódban:
- Az
Func<char, Task> asyncFuncegyes iterációkra vár, és átadja az aktuálisatletter. - Az összes betű feldolgozása után a rendszer egy üres sort ír a konzolra.
Ha elemeket szeretne hozzáadni a gyorsítótárhoz, hívja meg az Createegyik , vagy Set API-t:
var addLettersToCacheTask = IterateAlphabetAsync(letter =>
{
MemoryCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
_ = options.RegisterPostEvictionCallback(OnPostEviction);
AlphabetLetter alphabetLetter =
cache.Set(
letter, new AlphabetLetter(letter), options);
Console.WriteLine($"{alphabetLetter.Letter} was cached.");
return Task.Delay(
TimeSpan.FromMilliseconds(MillisecondsDelayAfterAdd));
});
await addLettersToCacheTask;
Az előző C# kódban:
- A változó
addLettersToCacheTaskaIterateAlphabetAsync-re delegálva várakozik. - A
Func<char, Task> asyncFuncérve egy lambdán alapul. - A
MemoryCacheEntryOptionspéldányosított példány abszolút lejárattal rendelkezik a jelenleg érvényeshez képest. - A rendszer regisztrál egy kilakoltatás utáni visszahívást.
- Az
AlphabetLetterobjektumot példányosítjuk, majd átadjuk Set-nak aletterésoptionselemekkel együtt. - A levélként gyorsítótárba helyezett bejegyzés jelenik meg a konzolon.
- Végül egy Task.Delay lesz visszaadva.
Az ábécé minden betűje esetében gyorsítótár-bejegyzéseket írnak, amelyek lejárati idővel és törlés utáni visszahívással rendelkeznek.
A kilakoltatás utáni visszahívás a konzolra kiürített érték részleteit írja le:
static void OnPostEviction(
object key, object? letter, EvictionReason reason, object? state)
{
if (letter is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{alphabetLetter.Letter} was evicted for {reason}.");
}
};
Most, hogy a gyorsítótár feltöltődött, egy újabb hívásra IterateAlphabetAsync vár, de ezúttal a IMemoryCache.TryGetValue-t hívja meg.
var readLettersFromCacheTask = IterateAlphabetAsync(letter =>
{
if (cache.TryGetValue(letter, out object? value) &&
value is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{letter} is still in cache. {alphabetLetter.Message}");
}
return Task.CompletedTask;
});
await readLettersFromCacheTask;
Ha a cache tartalmazza a letter kulcsot, és az value egy AlphabetLetter példány, akkor kiíródik a konzolra. Amikor a letter kulcs nincs a gyorsítótárban, eltávolították, és meghívták a kilakoltatást követő visszahívást.
További bővítménymetelyek
Számos IMemoryCache kényelmi alapú bővítménymetalógussal rendelkezik, köztük aszinkron GetOrCreateAsync:
- CacheExtensions.Get
- CacheExtensions.GetOrCreate
- CacheExtensions.GetOrCreateAsync
- CacheExtensions.Set
- CacheExtensions.TryGetValue
Rakd össze az egészet
A teljes mintaalkalmazás-forráskód egy legfelső szintű program, és két NuGet-csomagot igényel:
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
using IHost host = builder.Build();
IMemoryCache cache =
host.Services.GetRequiredService<IMemoryCache>();
const int MillisecondsDelayAfterAdd = 50;
const int MillisecondsAbsoluteExpiration = 750;
static void OnPostEviction(
object key, object? letter, EvictionReason reason, object? state)
{
if (letter is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{alphabetLetter.Letter} was evicted for {reason}.");
}
};
static async ValueTask IterateAlphabetAsync(
Func<char, Task> asyncFunc)
{
for (char letter = 'A'; letter <= 'Z'; ++letter)
{
await asyncFunc(letter);
}
Console.WriteLine();
}
var addLettersToCacheTask = IterateAlphabetAsync(letter =>
{
MemoryCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
_ = options.RegisterPostEvictionCallback(OnPostEviction);
AlphabetLetter alphabetLetter =
cache.Set(
letter, new AlphabetLetter(letter), options);
Console.WriteLine($"{alphabetLetter.Letter} was cached.");
return Task.Delay(
TimeSpan.FromMilliseconds(MillisecondsDelayAfterAdd));
});
await addLettersToCacheTask;
var readLettersFromCacheTask = IterateAlphabetAsync(letter =>
{
if (cache.TryGetValue(letter, out object? value) &&
value is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{letter} is still in cache. {alphabetLetter.Message}");
}
return Task.CompletedTask;
});
await readLettersFromCacheTask;
await host.RunAsync();
file record AlphabetLetter(char Letter)
{
internal string Message =>
$"The '{Letter}' character is the {Letter - 64} letter in the English alphabet.";
}
Módosíthatja a MillisecondsDelayAfterAdd és MillisecondsAbsoluteExpiration értékeket, hogy megfigyelhesse a gyorsítótárazott bejegyzések lejáratával és kiürítésével kapcsolatos viselkedés változásait. Az alábbi mintakimenet a kód futtatásából származik. (A .NET-események nemdeterminisztikus jellege miatt a kimenet eltérő lehet.)
A was cached.
B was cached.
C was cached.
D was cached.
E was cached.
F was cached.
G was cached.
H was cached.
I was cached.
J was cached.
K was cached.
L was cached.
M was cached.
N was cached.
O was cached.
P was cached.
Q was cached.
R was cached.
S was cached.
T was cached.
U was cached.
V was cached.
W was cached.
X was cached.
Y was cached.
Z was cached.
A was evicted for Expired.
C was evicted for Expired.
B was evicted for Expired.
E was evicted for Expired.
D was evicted for Expired.
F was evicted for Expired.
H was evicted for Expired.
K was evicted for Expired.
L was evicted for Expired.
J was evicted for Expired.
G was evicted for Expired.
M was evicted for Expired.
N was evicted for Expired.
I was evicted for Expired.
P was evicted for Expired.
R was evicted for Expired.
O was evicted for Expired.
Q was evicted for Expired.
S is still in cache. The 'S' character is the 19 letter in the English alphabet.
T is still in cache. The 'T' character is the 20 letter in the English alphabet.
U is still in cache. The 'U' character is the 21 letter in the English alphabet.
V is still in cache. The 'V' character is the 22 letter in the English alphabet.
W is still in cache. The 'W' character is the 23 letter in the English alphabet.
X is still in cache. The 'X' character is the 24 letter in the English alphabet.
Y is still in cache. The 'Y' character is the 25 letter in the English alphabet.
Z is still in cache. The 'Z' character is the 26 letter in the English alphabet.
Mivel az abszolút lejárat (MemoryCacheEntryOptions.AbsoluteExpirationRelativeToNow) be van állítva, az összes gyorsítótárazott elemek végül eltávolításra kerülnek.
Munkavállalói szolgáltatás cache-kezelése
Az adatok gyorsítótárazásának egyik gyakori stratégiája a gyorsítótár frissítése a felhasználó adatszolgáltatásoktól függetlenül. A Worker Service-sablon kiváló példa, mivel a BackgroundService futtatás a másik alkalmazáskódtól függetlenül (vagy a háttérben) történik. Amikor egy alkalmazás elindul, amely a IHostedServicevégrehajtást üzemelteti, a megfelelő implementáció (ebben az esetben a BackgroundService "feldolgozó") ugyanabban a folyamatban fog futni. Ezeket a üzemeltetett szolgáltatásokat a bővítménymetóduson keresztül AddHostedService<THostedService>(IServiceCollection) regisztráljuk a DI-ben önállóan. Más szolgáltatások bármilyen szolgáltatási élettartammal regisztrálhatók a DI-ben.
Fontos
A szolgáltatás élettartamát fontos megérteni. Amikor meghívja AddMemoryCache az összes memóriabeli gyorsítótárazási szolgáltatás regisztrálására, a szolgáltatások singletonként lesznek regisztrálva.
Fotószolgáltatás forgatókönyve
Tegyük fel, hogy olyan fotószolgáltatást fejleszt, amely a HTTP-n keresztül elérhető külső API-ra támaszkodik. Ezek a fényképadatok nem változnak gyakran, de sok van belőle. Minden fényképet egy egyszerű record jelképez:
namespace CachingExamples.Memory;
public readonly record struct Photo(
int AlbumId,
int Id,
string Title,
string Url,
string ThumbnailUrl);
Az alábbi példában számos szolgáltatás regisztrálva van a DI-ben. Minden szolgáltatásnak egyetlen felelőssége van.
using CachingExamples.Memory;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient<CacheWorker>();
builder.Services.AddHostedService<CacheWorker>();
builder.Services.AddScoped<PhotoService>();
builder.Services.AddSingleton(typeof(CacheSignal<>));
using IHost host = builder.Build();
await host.StartAsync();
Az előző C# kódban:
- Az általános gazdagép alapértelmezésekkel van létrehozva.
- A memória alapú gyorsítótárazási szolgáltatások AddMemoryCache-val/-vel vannak regisztrálva.
- A rendszer regisztrál egy
HttpClientpéldányt aCacheWorkerkövetkező osztályhoz AddHttpClient<TClient>(IServiceCollection): . - Az
CacheWorkerosztály a AddHostedService<THostedService>(IServiceCollection)-hoz van regisztrálva. - Az
PhotoServiceosztály a AddScoped<TService>(IServiceCollection)-hoz van regisztrálva. - Az
CacheSignal<T>osztály a AddSingleton-hoz van regisztrálva. - A
hostpéldány a builderektől kerül létrehozásra és aszinkron módon indul el.
A PhotoService felelős azért, hogy olyan fényképeket szerezzen be, amelyek megfelelnek a megadott feltételeknek (vagy filter).
using Microsoft.Extensions.Caching.Memory;
namespace CachingExamples.Memory;
public sealed class PhotoService(
IMemoryCache cache,
CacheSignal<Photo> cacheSignal,
ILogger<PhotoService> logger)
{
public async IAsyncEnumerable<Photo> GetPhotosAsync(Func<Photo, bool>? filter = default)
{
try
{
await cacheSignal.WaitAsync();
Photo[] photos =
(await cache.GetOrCreateAsync(
"Photos", _ =>
{
logger.LogWarning("This should never happen!");
return Task.FromResult(Array.Empty<Photo>());
}))!;
// If no filter is provided, use a pass-thru.
filter ??= _ => true;
foreach (Photo photo in photos)
{
if (!default(Photo).Equals(photo) && filter(photo))
{
yield return photo;
}
}
}
finally
{
cacheSignal.Release();
}
}
}
Az előző C# kódban:
- A konstruktorhoz egy
IMemoryCache, egyCacheSignal<Photo>, ésILoggerszükséges. - A
GetPhotosAsyncmódszer:- Definiál egy paramétert
Func<Photo, bool> filter, és visszaad egyIAsyncEnumerable<Photo>. - A
_cacheSignal.WaitAsync()kiadásának meghívása és megvárása; ez biztosítja, hogy a gyorsítótár elérése előtt az fel legyen töltve. - Hívások
_cache.GetOrCreateAsync(), aszinkron módon leküldi az összes fényképet a gyorsítótárba. - Az
factoryargumentum naplóz egy figyelmeztetést, és üres fényképtömböt ad vissza – ez soha nem fordulhat elő. - A gyorsítótárban lévő összes fénykép ismételt ellenőrzésre kerül, szűrve és létrehozva
yield return-val. - Végül a gyorsítótár jelét alaphelyzetbe állítják.
- Definiál egy paramétert
A szolgáltatás felhasználói szabadon hívhatják fel a GetPhotosAsync metódust, és ennek megfelelően kezelhetik a fényképeket. Nem HttpClient szükséges, mivel a gyorsítótár tartalmazza a fényképeket.
Az aszinkron jel egy beágyazott SemaphoreSlim példányon alapul, egy általános típusú korlátozott szingli keretein belül. A CacheSignal<T> egy SemaphoreSlim példányra támaszkodik.
namespace CachingExamples.Memory;
public sealed class CacheSignal<T>
{
private readonly SemaphoreSlim _semaphore = new(1, 1);
/// <summary>
/// Exposes a <see cref="Task"/> that represents the asynchronous wait operation.
/// When signaled (consumer calls <see cref="Release"/>), the
/// <see cref="Task.Status"/> is set as <see cref="TaskStatus.RanToCompletion"/>.
/// </summary>
public Task WaitAsync() => _semaphore.WaitAsync();
/// <summary>
/// Exposes the ability to signal the release of the <see cref="WaitAsync"/>'s operation.
/// Callers who were waiting, will be able to continue.
/// </summary>
public void Release() => _semaphore.Release();
}
Az előző C#-kódban a dekorátorminta egy SemaphoreSlim példányának beburkolására szolgál. Mivel a CacheSignal<T> singletonként van regisztrálva, a szolgáltatás minden élettartama alatt használható bármilyen generikus típussal – ebben az esetben a Photo. A gyorsítótár előkészítésének jelzésére felelős.
A CacheWorker az BackgroundService egyik alosztálya.
using System.Net.Http.Json;
using Microsoft.Extensions.Caching.Memory;
namespace CachingExamples.Memory;
public sealed class CacheWorker(
ILogger<CacheWorker> logger,
HttpClient httpClient,
CacheSignal<Photo> cacheSignal,
IMemoryCache cache) : BackgroundService
{
private readonly TimeSpan _updateInterval = TimeSpan.FromHours(3);
private bool _isCacheInitialized = false;
private const string Url = "https://jsonplaceholder.typicode.com/photos";
public override async Task StartAsync(CancellationToken cancellationToken)
{
await cacheSignal.WaitAsync();
await base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Updating cache.");
try
{
Photo[]? photos =
await httpClient.GetFromJsonAsync<Photo[]>(
Url, stoppingToken);
if (photos is { Length: > 0 })
{
cache.Set("Photos", photos);
logger.LogInformation(
"Cache updated with {Count:#,#} photos.", photos.Length);
}
else
{
logger.LogWarning(
"Unable to fetch photos to update cache.");
}
}
finally
{
if (!_isCacheInitialized)
{
cacheSignal.Release();
_isCacheInitialized = true;
}
}
try
{
logger.LogInformation(
"Will attempt to update the cache in {Hours} hours from now.",
_updateInterval.Hours);
await Task.Delay(_updateInterval, stoppingToken);
}
catch (OperationCanceledException)
{
logger.LogWarning("Cancellation acknowledged: shutting down.");
break;
}
}
}
}
Az előző C# kódban:
- A konstruktorhoz egy
ILogger, egyHttpClient, ésIMemoryCacheszükséges. - A
_updateIntervalbeállítás három órán keresztül van definiálva. - A
ExecuteAsyncmódszer:- Hurkok az alkalmazás futtatása közben.
- HTTP-kérést készít
"https://jsonplaceholder.typicode.com/photos", és a választPhotoobjektumok tömbjeként leképezi. - A fényképek tömbje a
IMemoryCachekulcs alá"Photos"kerül. - A
_cacheSignal.Release()hívása felszabadítja a fogyasztókat, akik a jelre vártak. - Task.Delay hívásra a frissítési időköz miatt várakozás van.
- Három óra késleltetés után a gyorsítótár ismét frissül.
Az ugyanabban a folyamatban lévő felhasználók kérhetik a IMemoryCache fényképeket, de a gyorsítótár frissítéséért a CacheWorker felelős.
Hibrid gyorsítótárazás
A HybridCache kódtár egyesíti a memóriában és az elosztott gyorsítótárazásban nyújtott előnyöket, miközben a meglévő gyorsítótárazási API-kkal kezeli a gyakori kihívásokat. A .NET 9-ben bevezetett HybridCache egységes API leegyszerűsíti a gyorsítótárazás megvalósítását, és olyan beépített funkciókat tartalmaz, mint a stampede protection és a konfigurálható szerializálás.
Legfontosabb funkciók
HybridCache számos előnyt kínál a IMemoryCache és IDistributedCache külön használatával szemben:
- Kétszintű gyorsítótárazás: A memóriabeli (L1) és az elosztott (L2) gyorsítótárrétegek automatikus kezelése. Az adatokat a rendszer először a memóriabeli gyorsítótárból kéri le a sebesség érdekében, majd szükség esetén az elosztott gyorsítótárból, végül pedig a forrásból.
-
Stampede-védelem: Megakadályozza, hogy több egyidejű kérés, amely ugyanazt
HybridCachea példányt használja, ne hajtsa végre ugyanazt a drága műveletet. Csak egy kérés kéri le az adatokat, míg mások megvárják az eredményt. Ez a koordináció nem terjed ki másHybridCachepéldányokra, még akkor sem, ha ugyanazt az elosztott gyorsítótárat használják. - Konfigurálható szerializálás: Több szerializálási formátumot támogat, beleértve a JSON (alapértelmezett), a protobuf és az XML formátumot.
- Címkealapú érvénytelenítés: Csoportosítja a kapcsolódó gyorsítótár-bejegyzéseket címkékkel a hatékony kötegelt érvénytelenítés érdekében.
-
Egyszerűsített API: A
GetOrCreateAsyncmetódus automatikusan kezeli a gyorsítótárhibákat, a szerializálást és a tárolást.
Mikor érdemes használni a HybridCache-t?
Fontolja meg a következő esetekben történő használatát HybridCache :
- Helyi (memórián belüli) és elosztott gyorsítótárazásra is szükség van egy többkiszolgálós környezetben.
- Védelmet szeretne a gyorsítótári roham forgatókönyvek ellen.
- Ön az egyszerűsített API-t részesíti előnyben ahelyett, hogy manuálisan koordinálna
IMemoryCacheésIDistributedCache. - A kapcsolódó bejegyzésekhez címkealapú gyorsítótár-érvénytelenítésre van szükség.
Jótanács
Az egyszerű gyorsítótárazási igényekkel rendelkező egykiszolgálós alkalmazások esetében elegendő lehet a memóriabeli gyorsítótárazás . Többkiszolgálós alkalmazások esetén, ahol nincs szükség tömeges hozzáférés elleni védelemre vagy címkealapú érvénytelenítésre, fontolja meg az elosztott gyorsítótárazást.
HybridCache beállítása
A használathoz HybridCachetelepítse a Microsoft.Extensions.Caching.Hybrid NuGet-csomagot:
dotnet add package Microsoft.Extensions.Caching.Hybrid
Regisztrálja a szolgáltatást a HybridCache DI-ben a következő hívással AddHybridCache:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHybridCache();
Az előző kód alapértelmezett beállításokkal regisztrálja a HybridCache . Globális beállításokat is konfigurálhat:
var builderWithOptions = Host.CreateApplicationBuilder(args);
builderWithOptions.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024; // 1 MB
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(2)
};
});
Alapszintű használat
Az elsődleges módszer a HybridCache-val való kapcsolatfelvételre a GetOrCreateAsync. Ez a metódus a megadott kulccsal ellenőrzi a gyorsítótárat, és ha nem található, meghívja a gyári metódust az adatok lekérésére:
async Task<WeatherData> GetWeatherDataAsync(HybridCache cache, string city)
{
return await cache.GetOrCreateAsync(
$"weather:{city}",
async cancellationToken =>
{
// Simulate fetching from an external API
await Task.Delay(100, cancellationToken);
return new WeatherData(city, 72, "Sunny");
}
);
}
Az előző C# kódban:
- A
GetOrCreateAsyncmetódus egy egyedi kulcsot és egy gyári metódust használ. - Ha az adatok nincsenek a gyorsítótárban, a rendszer meghívja a gyári metódust az adatok lekérésére.
- A rendszer automatikusan tárolja az adatokat a memóriában és az elosztott gyorsítótárakban is.
- Csak egy egyidejű kérés hajtja végre a gyári metódust; mások várnak az eredményre.
Belépési beállítások
Az adott gyorsítótár-bejegyzések globális alapértelmezett beállításait felülbírálhatja a következővel HybridCacheEntryOptions:
async Task<WeatherData> GetWeatherWithOptionsAsync(HybridCache cache, string city)
{
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
return await cache.GetOrCreateAsync(
$"weather:{city}",
async cancellationToken => new WeatherData(city, 72, "Sunny"),
entryOptions
);
}
A belépési beállítások lehetővé teszik a konfigurálást:
- HybridCacheEntryOptions.Expiration: Meddig kell a bejegyzést gyorsítótárban tárolni az elosztott gyorsítótár segítségével.
- HybridCacheEntryOptions.LocalCacheExpiration: Mennyi ideig kell a bejegyzést gyorsítótárazni a helyi memóriában?
- HybridCacheEntryOptions.Flags: További jelzők a gyorsítótár viselkedésének szabályozásához.
Címkealapú érvénytelenítés
A címkék lehetővé teszik a kapcsolódó gyorsítótár-bejegyzések csoportosítását és érvénytelenítését. Ez olyan helyzetekben hasznos, amikor a kapcsolódó adatokat egységként kell frissíteni:
async Task<CustomerData> GetCustomerAsync(HybridCache cache, int customerId)
{
var tags = new[] { "customer", $"customer:{customerId}" };
return await cache.GetOrCreateAsync(
$"customer:{customerId}",
async cancellationToken => new CustomerData(customerId, "John Doe", "john@example.com"),
new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(30) },
tags
);
}
Egy adott címkével rendelkező összes bejegyzés érvénytelenítése:
async Task InvalidateCustomerCacheAsync(HybridCache cache, int customerId)
{
await cache.RemoveByTagAsync($"customer:{customerId}");
}
Egyszerre több címkét is érvényteleníthet:
async Task InvalidateAllCustomersAsync(HybridCache cache)
{
await cache.RemoveByTagAsync(new[] { "customer", "orders" });
}
Megjegyzés:
A címkealapú érvénytelenítés logikai művelet. Nem távolítja el aktívan az értékeket a gyorsítótárból, de biztosítja, hogy a címkézett bejegyzések gyorsítótárhibákként legyenek kezelve. A bejegyzések végül a konfigurált élettartamuk alapján lejárnak.
Gyorsítótár-bejegyzések eltávolítása
Ha kulcs alapján szeretne eltávolítani egy adott gyorsítótár-bejegyzést, használja a RemoveAsync következő módszert:
async Task RemoveWeatherDataAsync(HybridCache cache, string city)
{
await cache.RemoveAsync($"weather:{city}");
}
Az összes gyorsítótárazott bejegyzés érvénytelenítéséhez használja a fenntartott helyettesítő karaktert "*":
async Task InvalidateAllCacheAsync(HybridCache cache)
{
await cache.RemoveByTagAsync("*");
}
Szerializáció
Elosztott gyorsítótárazási forgatókönyvekhez szükséges a HybridCache szerializálása. Alapértelmezés szerint belsőleg kezeli a string-t és a byte[]-t, és a System.Text.Json-t más típusokhoz használja. Egyéni szerializálókat konfigurálhat adott típusokhoz, vagy általános célú szerializálót is használhat:
// Custom serialization example
// Note: This requires implementing a custom IHybridCacheSerializer<T>
var builderWithSerializer = Host.CreateApplicationBuilder(args);
builderWithSerializer.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
// To add a custom serializer, uncomment and provide your implementation:
// .AddSerializer<WeatherData, CustomWeatherDataSerializer>();
Elosztott gyorsítótár konfigurálása
HybridCache A konfigurált implementációt IDistributedCache használja az elosztott (L2) gyorsítótárhoz. Még IDistributedCache konfiguráció nélkül is biztosít a HybridCache memórián belüli gyorsítótárazást és túlterhelés elleni védelmet. A Redis hozzáadása elosztott gyorsítótárként:
// Distributed cache with Redis
var builderWithRedis = Host.CreateApplicationBuilder(args);
builderWithRedis.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
builderWithRedis.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
Az elosztott gyorsítótár-implementációkkal kapcsolatos további információkért lásd az elosztott gyorsítótárazást.
Elosztott cache
Bizonyos esetekben elosztott gyorsítótárra van szükség – ez több alkalmazáskiszolgáló esetében is így van. Az elosztott gyorsítótárak a memóriabeli gyorsítótárazási módszernél nagyobb mértékű felskálázást támogatnak. Az elosztott gyorsítótár használata kiosztja a gyorsítótár memóriáját egy külső folyamatba, de extra hálózati I/O-t igényel, és egy kicsit nagyobb késést eredményez (még akkor is, ha névleges).
Az elosztott gyorsítótárazási absztrakciók a Microsoft.Extensions.Caching.Memory NuGet-csomag részét képezik, és még egy AddDistributedMemoryCache bővítménymetódus is létezik.
Figyelmeztetés
AddDistributedMemoryCache csak fejlesztési vagy tesztelési forgatókönyvekben használható, és nem életképes éles környezetbeli megvalósításhoz.
Vegye figyelembe az alábbi csomagok bármelyik elérhető implementációját IDistributedCache :
Microsoft.Extensions.Caching.SqlServerMicrosoft.Extensions.Caching.StackExchangeRedisMicrosoft.Extensions.Caching.CosmosMicrosoft.Extensions.Caching.PostgresNCache.Microsoft.Extensions.Caching.OpenSource
Elosztott gyorsítótárazási API
Az elosztott gyorsítótárazási API-k valamivel primitívebbek, mint a memórián belüli gyorsítótárazási API-k. A kulcs-érték párok egy kicsit alaposabbak. A memóriabeli kulcsok alapja egy object, míg az elosztott kulcsok egy string. A memóriabeli gyorsítótárazás esetén az érték bármilyen erősen típusos általános típus lehet, míg az elosztott gyorsítótárazásban lévő értékek következőként tartósítva: byte[]. Ez nem jelenti azt, hogy a különböző implementációk nem teszik közzé az erős típusú generikus értékeket, de ez egy implementációs részlet.
Értékek létrehozása
Ha értékeket szeretne létrehozni az elosztott gyorsítótárban, hívja meg a beállított API-k egyikét:
AlphabetLetter A memóriabeli gyorsítótár példájából származó rekord használatával szerializálhatja az objektumot JSON formátumúvá, majd kódolhatja mint string:
DistributedCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
AlphabetLetter alphabetLetter = new(letter);
string json = JsonSerializer.Serialize(alphabetLetter);
byte[] bytes = Encoding.UTF8.GetBytes(json);
await cache.SetAsync(letter.ToString(), bytes, options);
A memóriabeli gyorsítótárazáshoz hasonlóan a gyorsítótárbejegyzések is segíthetnek a gyorsítótárban való létezésük finomhangolásában – ebben az esetben a DistributedCacheEntryOptions.
Bővítménymetelyek létrehozása
Az értékek létrehozásához számos kényelmi alapú bővítménymetely létezik. Ezek a módszerek segítenek elkerülni az objektumok kódokká való átalakításátstring:byte[]
Értékek olvasása
Ha az elosztott gyorsítótárból szeretne értékeket olvasni, hívja meg az Get egyik API-t:
AlphabetLetter? alphabetLetter = null;
byte[]? bytes = await cache.GetAsync(letter.ToString());
if (bytes is { Length: > 0 })
{
string json = Encoding.UTF8.GetString(bytes);
alphabetLetter = JsonSerializer.Deserialize<AlphabetLetter>(json);
}
Miután kiolvasott egy gyorsítótárbejegyzést a gyorsítótárból, lekérheti az UTF8 kódolt reprezentációt string a byte[]gyorsítótárból.
A bővítmények olvasási módszerei
Az értékek olvasásához számos kényelmi alapú bővítménymetely létezik. Ezek a módszerek segítenek elkerülni, hogy a byte[] objektum string ábrázolássá legyen dekódolva.
Értékek frissítése
Az elosztott gyorsítótár értékeit egyetlen API-hívással nem lehet frissíteni. Ehelyett az értékek a frissítési API-k egyikével visszaállíthatják a csúszó lejárati idejüket:
Ha a tényleges értéket frissíteni kell, törölnie kell az értéket, majd újra hozzá kell adnia.
Értékek törlése
Az elosztott gyorsítótár értékeinek törléséhez hívja meg az Remove egyik API-t:
Jótanács
Bár ezeknek az API-knak szinkron verziói vannak, vegye figyelembe, hogy az elosztott gyorsítótárak implementációi a hálózati I/O-ra támaszkodnak. Ezért általában előnyösebb az aszinkron API-k használata.