Gyorsítótár a memóriában az ASP.NET Core-ban

Rick Anderson, John Luo és Steve Smith

A gyorsítótárazás jelentősen javíthatja az alkalmazások teljesítményét és méretezhetőségét a tartalom létrehozásához szükséges munka csökkentésével. A gyorsítótárazás akkor működik a legjobban, ha az adatok ritkán változnak és költséges a létrehozásuk. Az adatok másolatát készíti el a gyorsítótárazás, ami sokkal gyorsabban visszaadható, mint az eredeti forrásból. Az alkalmazásokat úgy kell írni és tesztelni, hogy soha ne függjenek a gyorsítótárazott adatoktól.

ASP.NET Core számos különböző gyorsítótárat támogat. A legegyszerűbb gyorsítótár a IMemoryCache interfészen alapul. IMemoryCache a webkiszolgáló memóriájában tárolt gyorsítótárat jelöli. A szerverfarmon (több kiszolgálón) futó alkalmazásoknak biztosítaniuk kell, hogy a munkamenetek ragadósak legyenek a memóriában történő gyorsítótárazás során. A ragadós munkamenetek biztosítják, hogy az ügyféltől érkező kérések mind ugyanarra a kiszolgálóra menjenek. Egy Azure-webalkalmazás például az Microsoft application request Routing (ARR) használatával irányítja az összes kérést ugyanarra a kiszolgálóra.

A webfarmok nem ragadós munkamenetei az elosztott gyorsítótárat igénylik a gyorsítótár konzisztenciájával kapcsolatos problémák elkerülése érdekében. Egyes alkalmazások esetében az elosztott gyorsítótárak nagyobb mértékű felskálázást támogatnak, mint a memóriabeli gyorsítótárak. Elosztott gyorsítótár használata kiosztja a gyorsítótár memóriáját egy külső folyamatba.

A memóriabeli gyorsítótár bármilyen objektumot tárolhat. Az elosztott gyorsítótár-illesztő korlátozva a byte[]-re. A memóriabeli és az elosztott gyorsítótár kulcs-érték párként tárolja a gyorsítótár elemeit.

Használja a System.Runtime.Caching/MemoryCache-t

System.Runtime.Caching / MemoryCache (NuGet-csomag) a következőkkel használható:

  • .NET Standard 2.0 vagy újabb verzió
  • Minden .NET implementáció, amely .NET Standard 2.0-s vagy újabb verziót célozza (például ASP.NET Core 3.1 vagy újabb)
  • .NET-keretrendszer 4.5-ös vagy újabb verziója

Microsoft.Extensions.Caching.Memory/IMemoryCache (ebben a cikkben ismertetett) ajánlott a System.Runtime.Caching/MemoryCache helyett, mert jobb integrációt biztosít az ASP.NET Core-ral. Például IMemoryCache natív módon működik ASP.NET Core függőséginjektálással.

Kompatibilitási hídként használható System.Runtime.Caching/MemoryCache a kód ASP.NET 4.x-ből ASP.NET Core-ba történő portolásához.

A memóriabeli gyorsítótárazással kapcsolatos irányelvek áttekintése

A memóriabeli gyorsítótárazásra az alábbi irányelvek vonatkoznak:

  • A kódnak mindig rendelkeznie kell tartalék beállítással az adatok lekéréséhez, és nem függ a gyorsítótárazott érték rendelkezésre állásától.

  • A gyorsítótár memóriát használ, ami szűkös erőforrás. A gyorsítótár növekedésének korlátozása:

    • Ne szúrjon be külső bemenetet a gyorsítótárba. Például nem ajánlott tetszőleges, felhasználó által megadott bemenetet gyorsítótárkulcsként használni, mert a bemenet kiszámíthatatlan mennyiségű memóriát használhat fel.

    • A gyorsítótár növekedésének korlátozásához használjon lejáratokat.

    • A Gyorsítótár méretének korlátozásához használja a SetSize, a Size és a SizeLimit parancsot. Az ASP.NET Core futtatókörnyezet nem korlátozza a gyorsítótár méretét memóriaterhelés alapján. A fejlesztő felelős a gyorsítótár méretének korlátozásáért.

IMemoryCache-példány létrehozása

A memóriabeli gyorsítótárazás egy szolgáltatás, amelyet az alkalmazás függőségi injektálás segítségével hivatkozik meg.

Warning

Ha ugyanazt a gyorsítótárat több keretrendszer vagy tár használja, az egy megosztott gyorsítótár. Ha megosztott memóriagyorsítótárat használ a dependency injection segítségével, és SetSize, Size, valamint SizeLimit-t is alkalmaz a gyorsítótár méretének korlátozására, az alkalmazás meghibásodhat.

Ha a gyorsítótárban méretkorlát van beállítva, az összes bejegyzésnek meg kell adnia a méretet a hozzáadásukkor. Ez a megközelítés problémákhoz vezethet, mert előfordulhat, hogy a fejlesztők nem tudják teljes mértékben szabályozni, hogy mi használja a megosztott gyorsítótárat.

A gyorsítótár méretének SetSize metódussal, a Size tulajdonsággal vagy a SizeLimit tulajdonsággal történő korlátozásához hozzon létre egy singleton-t a gyorsítótárazáshoz. További információ és példa: A SetSize, a Size és a SizeLimit használata a gyorsítótár méretének korlátozásához.

Kérje le a példányt IMemoryCache a konstruktorban:

public class IndexModel : PageModel
{
    private readonly IMemoryCache _memoryCache;

    public IndexModel(IMemoryCache memoryCache) =>
        _memoryCache = memoryCache;

    // ...

Az alábbi kód a TryGetValue metódus használatával ellenőrzi, hogy van-e idő a gyorsítótárban. Ha egy időpont nincs gyorsítótárazva, a rendszer létrehoz egy új bejegyzést, és hozzáadja a gyorsítótárhoz a Set következő módszerrel:

public void OnGet()
{
    CurrentDateTime = DateTime.Now;

    if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
    {
        cacheValue = CurrentDateTime;

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
    }

    CacheCurrentDateTime = cacheValue;
}

Az előző kódban a gyorsítótár-bejegyzés három másodperces csúszó lejárati idővel lett konfigurálva. Ha a gyorsítótár-bejegyzés 3 másodpercnél hosszabb ideig nem érhető el, a bejegyzés ki lesz távolítva a gyorsítótárból. A gyorsítótár-bejegyzés minden elérésekor további 3 másodpercig a gyorsítótárban marad. Az CacheKeys osztály a letöltési minta része.

Az aktuális és a gyorsítótárazott idő jelenik meg:

<ul>
    <li>Current Time: @Model.CurrentDateTime</li>
    <li>Cached Time: @Model.CacheCurrentDateTime</li>
</ul>

A következő kód a Set bővítménymetódus használatával gyorsítótárazza az adatokat egy relatív időtartamra e nélkül MemoryCacheEntryOptions.

_memoryCache.Set(CacheKeys.Entry, DateTime.Now, TimeSpan.FromDays(1));

Az előző kódban a gyorsítótár-bejegyzés egy nap relatív lejáratával van konfigurálva. A gyorsítótár-bejegyzést a rendszer egy nap elteltével kiüríti a gyorsítótárból, még akkor is, ha a bejegyzés az időtúllépési időszak alatt érhető el.

Az alábbi kód a GetOrCreate és GetOrCreateAsync metódusok használatával gyorsítótárazza az adatokat.

public void OnGetCacheGetOrCreate()
{
    var cachedValue = _memoryCache.GetOrCreate(
        CacheKeys.Entry,
        cacheEntry =>
        {
            cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return DateTime.Now;
        });

    // ...
}

public async Task OnGetCacheGetOrCreateAsync()
{
    var cachedValue = await _memoryCache.GetOrCreateAsync(
        CacheKeys.Entry,
        cacheEntry =>
        {
            cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    // ...
}

A következő kód meghívja a(z) Get metódust a gyorsítótárazott idő beolvasásához.

var cacheEntry = _memoryCache.Get<DateTime?>(CacheKeys.Entry);

A következő kód egy gyorsítótárazott elemet kap vagy hoz létre abszolút lejárati idővel.

var cachedValue = _memoryCache.GetOrCreate(
    CacheKeys.Entry,
    cacheEntry =>
    {
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

A csak csúszó lejárattal rendelkező gyorsítótárazott elemek esetében fennáll a veszélye annak, hogy soha nem jár le. Ha a gyorsítótárazott elemet a csúszó lejárati időszakon belül ismételten elérik, az elem soha nem jár le. A csúszó lejárat és az abszolút lejárat kombinációja garantálja az elem lejáratát. Az abszolút lejárat határozza meg az elem tárolási idejének felső határát. Továbbra is lehetővé teszi, hogy az elem korábban lejárjon, ha az elem nem kérhető le a csúszásos lejárati időközön belül. Ha a csúszó lejárati időköz vagy az abszolút lejárati idő lejár, a rendszer kizárja az elemet a gyorsítótárból.

Az alábbi kód lekér egy gyorsítótárba helyezett elemet, vagy létrehoz egyet csúszó és abszolút lejárattal.

var cachedValue = _memoryCache.GetOrCreate(
    CacheKeys.CallbackEntry,
    cacheEntry =>
    {
        cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

Az előző kód garantálja, hogy az adatok gyorsítótárazása nem történik meg a megadott időtartamnál hosszabb ideig.

GetOrCreate, GetOrCreateAsyncés Get az osztályban a CacheExtensions bővítménymetelyek. Ezek a metódusok kibővítik a IMemoryCache képességeit.

MemoryCacheEntryOptions létrehozása bejegyzéshez

Az alábbi példa megmutatja, hogyan hozható létre MemoryCacheEntryOptions egy bejegyzés. A kód a következő feladatokat hajtja végre:

  • A gyorsítótár prioritását a következőre CacheItemPriority.NeverRemoveállítja: .

  • Beállít egy PostEvictionDelegate hívást, miután a bejegyzést kiürítette a gyorsítótárból. A visszahívás egy másik szálon fut, mint a kód, amely eltávolítja az elemet a gyorsítótárból.

public void OnGetCacheRegisterPostEvictionCallback()
{
    var memoryCacheEntryOptions = new MemoryCacheEntryOptions()
        .SetPriority(CacheItemPriority.NeverRemove)
        .RegisterPostEvictionCallback(PostEvictionCallback, _memoryCache);

    _memoryCache.Set(CacheKeys.CallbackEntry, DateTime.Now, memoryCacheEntryOptions);
}

private static void PostEvictionCallback(
    object cacheKey, object cacheValue, EvictionReason evictionReason, object state)
{
    var memoryCache = (IMemoryCache)state;

    memoryCache.Set(
        CacheKeys.CallbackMessage,
        $"Entry {cacheKey} was evicted: {evictionReason}.");
}

Gyorsítótár méretének korlátozása a SetSize, a Size és a SizeLimit használatával

A MemoryCache példány opcionálisan megadhatja és kikényszerítheti a méretkorlátot. A gyorsítótár méretkorlátja nem rendelkezik meghatározott mértékegységekkel, mert a gyorsítótár nem rendelkezik a bejegyzések méretének mérésére szolgáló mechanizmussal. Ha a gyorsítótár méretkorlátja be van állítva, minden bejegyzésnek meg kell adnia a méretet. A ASP.NET Core-futtatókörnyezet nem korlátozza a gyorsítótár méretét a memóriaterhelés alapján. A gyorsítótár méretének korlátozása a fejlesztőn múlik. A megadott méret a fejlesztő által választott egységekben van megadva.

Például:

  • Ha a webalkalmazás elsősorban karakterláncokat gyorsítótáraz, akkor a gyorsítótár minden egyes bejegyzésének mérete lehet a karakterlánc hossza.
  • Az alkalmazás az összes bejegyzés méretét 1-ként adhatja meg, a méretkorlát pedig a bejegyzések száma.

Ha a SizeLimit tulajdonság nincs beállítva, a gyorsítótár kötöttség nélkül nő. A ASP.NET Core-futtatókörnyezet nem csökkenti a gyorsítótárat, ha a rendszermemória alacsony. Az alkalmazásokat a következőre kell létrehozni:

  • Korlátozza a gyorsítótár növekedését.
  • Hívja meg a metódust Compact , Remove ha a rendelkezésre álló memória korlátozott.

Az alábbi kód létrehoz egy egység nélküli rögzített méretű MemoryCache példányt, amely függőséginjektálással érhető el:

public class MyMemoryCache
{
    public MemoryCache Cache { get; } = new MemoryCache(
        new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
}

A SizeLimit tulajdonság nem rendelkezik egységekkel. A gyorsítótárazott bejegyzéseknek meg kell adniuk a méretet minden olyan egységben, amelyet a gyorsítótár méretkorlátjának beállításakor a legmegfelelőbbnek tartanak. A gyorsítótárpéldány minden felhasználójának ugyanazt az egységrendszert kell használnia. A bejegyzés nem lesz gyorsítótárazva, ha a gyorsítótárazott bejegyzésméretek összege meghaladja a megadott SizeLimitértéket. Ha nincs gyorsítótárméretkorlát beállítva, a rendszer figyelmen kívül hagyja a bejegyzés gyorsítótárméret-beállítását.

A következő kód regisztrálja a MyMemoryCache példányt a függőséginjektálási tárolóban.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddSingleton<MyMemoryCache>();

MyMemoryCache független memória-gyorsítótárként kerül megszervezésre olyan összetevők számára, amelyek tisztában vannak ezzel a méretkorlátos gyorsítótárral, és tudják, hogyan lehet megfelelően beállítani a gyorsítótár-bejegyzés méretét.

A gyorsítótár-bejegyzés mérete a bővítménymetódus vagy a SetSizeSize tulajdonság használatával állítható be:

if (!_myMemoryCache.Cache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        .SetSize(1);

    // cacheEntryOptions.Size = 1;

    _myMemoryCache.Cache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
}

Az előző kódban a két kiemelt sor ugyanazt az eredményt éri el, mint a gyorsítótár-bejegyzés méretének beállítása. A SetSize metódus a hívások new MemoryCacheOptions()láncolásakor nyújt kényelmes megoldást.

Memóriatár elemek eltávolítása a MemoryCache.Compact segítségével

A MemoryCache.Compact metódus a gyorsítótár megadott százalékos arányát a következő sorrendben próbálja eltávolítani:

  • Minden lejárt elem
  • Elemek prioritás szerint, ahol először a legalacsonyabb prioritású elemek kerülnek eltávolításra
  • Legutóbb használt objektumok
  • A legkorábbi abszolút lejáratú elemek
  • A legkorábbi csúszó lejáratú elemek

A prioritással NeverRemove ellátott rögzített elemek soha nem lesznek eltávolítva. Az alábbi kód eltávolít egy gyorsítótár-elemet, és meghívja a metódust a Compact gyorsítótárazott bejegyzések 25% eltávolítására:

_myMemoryCache.Cache.Remove(CacheKeys.Entry);
_myMemoryCache.Cache.Compact(.25);

További információt a GitHub Kompakt forrásában talál.

Gyorsítótár-bejegyzés eltávolítása lejárt függőségekkel

Az alábbi minta bemutatja, hogyan lehet lejáratni egy gyorsítótár-bejegyzést, ha egy függő bejegyzés lejár. A CancellationChangeToken hozzáadásra kerül a gyorsítótárazott elemhez. Amikor a Cancel metódust meghívja az CancellationTokenSource objektumon, a rendszer mindkét gyorsítótár-bejegyzést kiüríti:

public void OnGetCacheCreateDependent()
{
    var cancellationTokenSource = new CancellationTokenSource();

    _memoryCache.Set(
        CacheKeys.DependentCancellationTokenSource,
        cancellationTokenSource);

    using var parentCacheEntry = _memoryCache.CreateEntry(CacheKeys.Parent);

    parentCacheEntry.Value = DateTime.Now;

    _memoryCache.Set(
        CacheKeys.Child,
        DateTime.Now,
        new CancellationChangeToken(cancellationTokenSource.Token));
}

public void OnGetCacheRemoveDependent()
{
    var cancellationTokenSource = _memoryCache.Get<CancellationTokenSource>(
        CacheKeys.DependentCancellationTokenSource);

    cancellationTokenSource.Cancel();
}

Amikor egy CancellationTokenSource objektumot használ, csoportként több gyorsítótár-bejegyzést is kiüríthet. Az előző kódban található using mintával a using hatókörben létrehozott gyorsítótár-bejegyzések öröklik az eseményindítókat és a lejárati beállításokat.

A memóriabeli gyorsítótárazással kapcsolatos megjegyzések áttekintése

A memóriabeli gyorsítótárazásra az alábbi megjegyzések vonatkoznak:

  • A lejárat nem zajlik a háttérben.

    Nincs olyan időzítő, amely aktívan átvizsgálja a gyorsítótárat a lejárt elemek után. A gyorsítótárban (Get, TryGetValue, Set vagy Remove útján) végzett bármely tevékenység elindíthatja a lejárt elemek háttérvizsgálatát. Az CancellationTokenSource objektum időzítőjének beállítása (a CancelAfter módszer használatával) szintén eltávolítja a bejegyzést, és elindítja a lejárt elemek vizsgálatát.

    Az alábbi példa a regisztrált token CancellationTokenSource(TimeSpan) túlterhelt konstruktorát használja. Amikor ez a token aktiválódik, azonnal eltávolítja a bejegyzést, és végrehajtja az eltávolítási visszahívásokat.

    if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
    {
        cacheValue = DateTime.Now;
    
        var cancellationTokenSource = new CancellationTokenSource(
            TimeSpan.FromSeconds(10));
    
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            .AddExpirationToken(
                new CancellationChangeToken(cancellationTokenSource.Token))
            .RegisterPostEvictionCallback((key, value, reason, state) =>
            {
                ((CancellationTokenSource)state).Dispose();
            }, cancellationTokenSource);
    
        _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
    }
    
  • Ha visszahívást használ a gyorsítótárelem újratöltéséhez:

    • Előfordulhat, hogy több kérés észlelheti, hogy a gyorsítótárazott kulcs értéke üres, mivel a visszahívás még nem fejeződött be.
    • Ez a megközelítés több szál által a gyorsítótárazott elem újra feltöltését eredményezheti.
  • Amikor egy gyorsítótárbejegyzés (a szülő) létrehoz egy másik bejegyzést (a gyermek), a gyermek átmásolja a szülőbejegyzés lejárati jogkivonatait és az időalapú lejárati beállításokat. A gyermek nem jár le a szülőbejegyzés manuális eltávolításával vagy frissítésével.

  • PostEvictionCallbacks A tulajdonság használatával megadhatja, hogy mely visszahívások aktiválhatók a gyorsítótár-bejegyzés gyorsítótárból való kizárása után.

  • A legtöbb alkalmazás IMemoryCache esetében engedélyezve van. A AddMvc fájlban például a AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEngine, Add{Service}, és sok más metódus meghívása lehetővé teszi IMemoryCache.

    Az olyan alkalmazások esetében, amelyek nem hívják meg az említett Add{Service} módszerek egyikét, előfordulhat, hogy meg kell hívnia a metódust AddMemoryCache a Program.cs fájlban.

Háttérgyorsítótár-frissítés használata

A gyorsítótár frissítéséhez használjon háttérszolgáltatást , például az IHostedService interfészt. A háttérszolgáltatás csak akkor tudja újrakomponensíteni a bejegyzéseket, és hozzárendelni őket a gyorsítótárhoz, ha elkészültek.

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

Gyorsítótárazási alapismeretek

A gyorsítótárazás jelentősen javíthatja az alkalmazások teljesítményét és méretezhetőségét a tartalom létrehozásához szükséges munka csökkentésével. A gyorsítótárazás akkor működik a legjobban, ha az adatok ritkán változnak és költséges a létrehozásuk. Az adatok másolatát készíti el a gyorsítótárazás, ami sokkal gyorsabban visszaadható, mint az eredeti forrásból. Az alkalmazásokat úgy kell írni és tesztelni, hogy soha ne függjenek a gyorsítótárazott adatoktól.

ASP.NET Core számos különböző gyorsítótárat támogat. A legegyszerűbb gyorsítótár a IMemoryCache. IMemoryCache a webkiszolgáló memóriájában tárolt gyorsítótárat jelöli. A szerverfarmon (több kiszolgálón) futó alkalmazásoknak biztosítaniuk kell, hogy a munkamenetek ragadósak legyenek a memóriában történő gyorsítótárazás során. A ragadós munkamenetek biztosítják, hogy az ügyfél későbbi kérései mind ugyanarra a kiszolgálóra menjenek. Az Azure Web Apps például az Application Request Routing (ARR) használatával irányítja az összes további kérést ugyanarra a kiszolgálóra.

A webfarmok nem ragadós munkamenetei elosztott gyorsítótárat igényelnek a gyorsítótár konzisztenciaproblémáinak elkerülése érdekében. Egyes alkalmazások esetében az elosztott gyorsítótárak nagyobb mértékű felskálázást támogatnak, mint a memóriabeli gyorsítótárak. Elosztott gyorsítótár használata kiosztja a gyorsítótár memóriáját egy külső folyamatba.

A memóriabeli gyorsítótár bármilyen objektumot tárolhat. Az elosztott gyorsítótár-illesztő korlátozva a byte[]-re. A memóriabeli és az elosztott gyorsítótár kulcs-érték párként tárolja a gyorsítótár elemeit.

System.Runtime.Caching/MemoryCache

System.Runtime.Caching / MemoryCache (NuGet-csomag) a következőkkel használható:

  • .NET Standard 2.0 vagy újabb.
  • Minden .NET-implementáció , amely a .NET Standard 2.0-s vagy újabb verzióját célozza. Például ASP.NET Core 3.1-et vagy újabb verziót.
  • .NET-keretrendszer 4.5-ös vagy újabb verziója.

A Microsoft.Extensions.Caching.Memory/IMemoryCache (ebben a cikkben ismertetett) használata ajánlottSystem.Runtime.Caching/MemoryCache, mert jobban integrálható ASP.NET Core-ba. Például IMemoryCache natív módon működik ASP.NET Core függőséginjektálással.

Kompatibilitási hídként használható System.Runtime.Caching/MemoryCache a kód ASP.NET 4.x-ből ASP.NET Core-ba történő portolásához.

Gyorsítótárra vonatkozó irányelvek

  • A kódnak mindig rendelkeznie kell egy tartalék lehetőséggel az adatok lehívásához, és nem szabad attól függnie, hogy elérhető-e egy gyorsítótárazott érték.
  • A gyorsítótár kevés erőforrást, memóriát használ. A gyorsítótár növekedésének korlátozása:

Az IMemoryCache használata

Warning

Ha megosztott memória-gyorsítótárat használ a függőséginjektálásból, és hívja a(z) SetSize, Size vagy SizeLimit funkciókat a gyorsítótár méretének korlátozására, az alkalmazás meghibásodhat. Ha a gyorsítótárban méretkorlát van beállítva, a hozzáadáskor minden bejegyzésnek meg kell adnia a méretet. Ez problémákhoz vezethet, mivel előfordulhat, hogy a fejlesztők nem tudják teljes mértékben szabályozni, hogy mi használja a megosztott gyorsítótárat. Amikor a SetSize, Size vagy SizeLimit elemet használja a gyorsítótár korlátozására, hozzon létre egy gyorsítótár-egyton objektumot a gyorsítótárazáshoz. További információ és példa: A SetSize, a Size és a SizeLimit használata a gyorsítótár méretének korlátozásához. A megosztott gyorsítótárat más keretrendszerek vagy tárak osztják meg.

A memóriabeli gyorsítótárazás egy olyan szolgáltatás, amelyre egy függőséginjektálást használó alkalmazásból hivatkoznak. Kérje le a példányt IMemoryCache a konstruktorban:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

Az alábbi kód TryGetValue használja annak ellenőrzésére, hogy van-e az idő a gyorsítótárban. Ha egy időpont nincs gyorsítótárazva, a rendszer létrehoz egy új bejegyzést, és hozzáadja a gyorsítótárhoz a következővel Set: . Az CacheKeys osztály a letöltési minta része.

public static class CacheKeys
{
    public static string Entry => "_Entry";
    public static string CallbackEntry => "_Callback";
    public static string CallbackMessage => "_CallbackMessage";
    public static string Parent => "_Parent";
    public static string Child => "_Child";
    public static string DependentMessage => "_DependentMessage";
    public static string DependentCTS => "_DependentCTS";
    public static string Ticks => "_Ticks";
    public static string CancelMsg => "_CancelMsg";
    public static string CancelTokenSource => "_CancelTokenSource";
}
public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Save data in cache.
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

Az aktuális és a gyorsítótárazott idő jelenik meg:

@model DateTime?

<div>
    <h2>Actions</h2>
    <ul>
        <li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
        <li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAsynchronous">CacheGetOrCreateAsynchronous</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbs">CacheGetOrCreateAbs</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbsSliding">CacheGetOrCreateAbsSliding</a></li>

    </ul>
</div>

<h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>

A következő kód a Set bővítménymetódussal gyorsítótárazza az adatokat relatív ideig az MemoryCacheEntryOptions objektum létrehozása nélkül:

public IActionResult SetCacheRelativeExpiration()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Save data in cache and set the relative expiration time to one day
        _cache.Set(CacheKeys.Entry, cacheEntry, TimeSpan.FromDays(1));
    }

    return View("Cache", cacheEntry);
}

A gyorsítótárazott DateTime érték a gyorsítótárban marad, amíg az időtúllépési időszakon belül kérések vannak.

Az alábbi kód GetOrCreate és GetOrCreateAsync használatával gyorsítótárba helyezi az adatokat.

public IActionResult CacheGetOrCreate()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    return View("Cache", cacheEntry);
}

Az alábbi kódhívások Get a gyorsítótárazott idő beolvasásához:

public IActionResult CacheGet()
{
    var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
    return View("Cache", cacheEntry);
}

A következő kód egy gyorsítótárazott elemet kap vagy hoz létre abszolút lejárati idővel.

public IActionResult CacheGetOrCreateAbs()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

A csak csúszó lejárattal rendelkező gyorsítótárazott elemek esetében fennáll a veszélye annak, hogy soha nem jár le. Ha a gyorsítótárazott elemet a csúszó lejárati időszakon belül ismételten elérik, az elem soha nem jár le. A csúszó lejárat és az abszolút lejárat kombinálásával garantálható az elem lejárata. Az abszolút lejárat egyrészt azt határozza meg, hogy az elem mennyi ideig tárolható, másrészt lehetővé teszi az elem korábban lejárjon, ha nem kérik a csúszó lejárati időszakon belül. Ha a csúszó lejárati időköz vagy az abszolút lejárati idő lejárt, a rendszer kizárja az elemet a gyorsítótárból.

Az alábbi kód lekér egy gyorsítótárba helyezett elemet, vagy létrehoz egyet csúszó és abszolút lejárattal.

public IActionResult CacheGetOrCreateAbsSliding()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SetSlidingExpiration(TimeSpan.FromSeconds(3));
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

Az előző kód garantálja, hogy az adatok az abszolút időnél hosszabb ideig nem lesznek gyorsítótárazva.

GetOrCreate, GetOrCreateAsyncés Get az osztályban a CacheExtensions bővítménymetelyek. Ezek a metódusok kibővítik a IMemoryCache képességeit.

MemoryCacheEntryOptions

A következő minta:

  • Eltolható lejárati időt állít be. A gyorsítótárazott elemhez hozzáférő kérések visszaállítják a csúszó lejárat idejét.
  • A gyorsítótár prioritását a következőre CacheItemPriority.NeverRemoveállítja: .
  • Beállít egy PostEvictionDelegate, amelyet a bejegyzés gyorsítótárból történő törlése után hív meg. A visszahívás a kódtól eltérő szálon fut, amely eltávolítja az elemet a gyorsítótárból.
public IActionResult CreateCallbackEntry()
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        // Pin to cache.
        .SetPriority(CacheItemPriority.NeverRemove)
        // Add eviction callback
        .RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

    _cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

    return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
    return View("Callback", new CallbackViewModel
    {
        CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
        Message = _cache.Get<string>(CacheKeys.CallbackMessage)
    });
}

public IActionResult RemoveCallbackEntry()
{
    _cache.Remove(CacheKeys.CallbackEntry);
    return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

A SetSize, a Size és a SizeLimit használata a gyorsítótár méretének korlátozásához

A MemoryCache példány opcionálisan megadhatja és kikényszerítheti a méretkorlátot. A gyorsítótár méretkorlátja nem rendelkezik meghatározott mértékegységekkel, mert a gyorsítótár nem rendelkezik a bejegyzések méretének mérésére szolgáló mechanizmussal. Ha a gyorsítótár méretkorlátja be van állítva, minden bejegyzésnek meg kell adnia a méretet. A ASP.NET Core-futtatókörnyezet nem korlátozza a gyorsítótár méretét a memóriaterhelés alapján. A gyorsítótár méretének korlátozása a fejlesztőn múlik. A megadott méret a fejlesztő által választott egységekben van megadva.

Például:

  • Ha a webalkalmazás elsősorban karakterláncok gyorsítótárazásával foglalkozik, minden gyorsítótárbejegyzés mérete lehet a karakterlánc hossza.
  • Az alkalmazás az összes bejegyzés méretét 1-ként határozhatja meg, a méretkorlát pedig a bejegyzések száma.

Ha SizeLimit nincs beállítva, a gyorsítótár kötöttség nélkül növekszik. A ASP.NET Core-futtatókörnyezet nem csökkenti a gyorsítótárat, ha a rendszermemória alacsony. Az alkalmazásokat a következőre kell létrehozni:

  • Korlátozza a gyorsítótár növekedését.
  • Hívás Compact vagy Remove ha a rendelkezésre álló memória korlátozott:

Az alábbi kód egy, MemoryCache elérhető, egység nélküli rögzített méretet hoz létre:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache 
{
    public MemoryCache Cache { get; private set; }
    public MyMemoryCache()
    {
        Cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
    }
}

SizeLimit nem rendelkezik egységekkel. A gyorsítótárazott bejegyzéseknek meg kell adniuk a méretet az általuk legmegfelelőbbnek ítélt egységekben, ha a gyorsítótár méretkorlátja be van állítva. A gyorsítótárpéldány minden felhasználójának ugyanazt az egységrendszert kell használnia. A rendszer nem gyorsítótárazza a bejegyzéseket, ha a gyorsítótárazott bejegyzésméretek összege meghaladja a megadott SizeLimitértéket. Ha nincs megadva a gyorsítótár méretkorlátja, a rendszer figyelmen kívül hagyja a bejegyzés gyorsítótárméret-beállítását.

A következő kód a MyMemoryCache elemet regisztrálja a függőségkezelő konténerben.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache önálló memória-gyorsítótárként jön létre azon összetevők számára, amelyek tisztában vannak ezzel a mérettel korlátozott gyorsítótárral, és tudják, hogyan kell megfelelően beállítani a gyorsítótár-bejegyzés méretét.

A következő kód MyMemoryCachehasznál:

public class SetSize : PageModel
{
    private MemoryCache _cache;
    public static readonly string MyKey = "_MyKey";

    public SetSize(MyMemoryCache memoryCache)
    {
        _cache = memoryCache.Cache;
    }

    [TempData]
    public string DateTime_Now { get; set; }

    public IActionResult OnGet()
    {
        if (!_cache.TryGetValue(MyKey, out string cacheEntry))
        {
            // Key not in cache, so get data.
            cacheEntry = DateTime.Now.TimeOfDay.ToString();

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                // Set cache entry size by extension method.
                .SetSize(1)
                // Keep in cache for this time, reset time if accessed.
                .SetSlidingExpiration(TimeSpan.FromSeconds(3));

            // Set cache entry size via property.
            // cacheEntryOptions.Size = 1;

            // Save data in cache.
            _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
        }

        DateTime_Now = cacheEntry;

        return RedirectToPage("./Index");
    }
}

A gyorsítótár-bejegyzés méretét a Size vagy a SetSize bővítménymetódusokkal lehet beállítani.

public IActionResult OnGet()
{
    if (!_cache.TryGetValue(MyKey, out string cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now.TimeOfDay.ToString();

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Set cache entry size by extension method.
            .SetSize(1)
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Set cache entry size via property.
        // cacheEntryOptions.Size = 1;

        // Save data in cache.
        _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
    }

    DateTime_Now = cacheEntry;

    return RedirectToPage("./Index");
}

MemoryCache.Compact

MemoryCache.Compact a gyorsítótár megadott százalékos arányát a következő sorrendben próbálja eltávolítani:

  • Minden lejárt tétel.
  • Elemek prioritás szerint. A rendszer először a legalacsonyabb prioritású elemeket távolítja el.
  • Legrégebben használt objektumok.
  • A legkorábbi abszolút lejáratú elemek.
  • A legkorábbi eltolható lejáratú elemek.

A prioritással NeverRemove ellátott rögzített elemek soha nem lesznek eltávolítva. A következő kód eltávolít egy gyorsítótár-elemet, és meghívja a következőt Compact:

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

További információt a GitHub Kompakt forrásában talál.

Gyorsítótár-függőségek

Az alábbi minta bemutatja, hogyan lehet lejáratni egy gyorsítótár-bejegyzést, ha egy függő bejegyzés lejár. A CancellationChangeToken hozzáadásra kerül a gyorsítótárazott elemhez. Amikor a Cancel hívja meg a CancellationTokenSource-t, mindkét gyorsítótár-bejegyzés kiürül.

public IActionResult CreateDependentEntries()
{
    var cts = new CancellationTokenSource();
    _cache.Set(CacheKeys.DependentCTS, cts);

    using (var entry = _cache.CreateEntry(CacheKeys.Parent))
    {
        // expire this entry if the dependant entry expires.
        entry.Value = DateTime.Now;
        entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);

        _cache.Set(CacheKeys.Child,
            DateTime.Now,
            new CancellationChangeToken(cts.Token));
    }

    return RedirectToAction("GetDependentEntries");
}

public IActionResult GetDependentEntries()
{
    return View("Dependent", new DependentViewModel
    {
        ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
        ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
        Message = _cache.Get<string>(CacheKeys.DependentMessage)
    });
}

public IActionResult RemoveChildEntry()
{
    _cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
    return RedirectToAction("GetDependentEntries");
}

private static void DependentEvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Parent entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

CancellationTokenSource használatával több gyorsítótárbejegyzés is eltávolítható csoportként. using A fenti kódban szereplő mintával a blokkban létrehozott gyorsítótárbejegyzések öröklik az eseményindítókat és a using lejárati beállításokat.

További megjegyzések

  • A lejárat nem zajlik a háttérben. Nincs olyan időzítő, amely aktívan megvizsgálja a gyorsítótárat a lejárt elemek után. A gyorsítótárban (Get, , SetRemove) végzett bármely tevékenység elindíthatja a lejárt elemek háttérvizsgálatát. A CancellationTokenSource időzítő a CancelAfter rendszeren is eltávolítja a bejegyzést, és elindítja a lejárt elemek vizsgálatát. Az alábbi példa a CancellationTokenSource(TimeSpan) regisztrált tokent használja. Amikor ez a token aktiválódik, azonnal eltávolítja a bejegyzést, és aktiválja az eltávolítási visszahívásokat.

    public IActionResult CacheAutoExpiringTryGetValueSet()
    {
        DateTime cacheEntry;
    
        if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
        {
            cacheEntry = DateTime.Now;
    
            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
    
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .AddExpirationToken(new CancellationChangeToken(cts.Token));
    
            _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
        }
    
        return View("Cache", cacheEntry);
    }
    
  • Ha visszahívást használ a gyorsítótárelem újratöltéséhez:

    • Több kérés esetén is előfordulhat, hogy a gyorsítótárban lévő kulcs értéke üres, mert a visszahívás még nem fejeződött be.
    • Ez azt eredményezheti, hogy több szál is feltölti a gyorsítótárazott elemet.
  • Amikor egy gyorsítótár-bejegyzést használ egy másik létrehozásához, a gyermek átmásolja a szülőbejegyzés lejárati jogkivonatait és az időalapú lejárati beállításokat. A gyermek nem járt le a szülőbejegyzés manuális eltávolításával vagy frissítésével.

  • Az PostEvictionCallbacks a visszahívások beállítására használható, amelyek a gyorsítótár-bejegyzés gyorsítótárból való törlése után aktiválódnak. A példakódban a CancellationTokenSource.Dispose() van meghívva a CancellationTokenSource által használt nem felügyelt erőforrások felszabadítására. A CancellationTokenSource azonban nem kerül azonnal eltávolításra, mert a gyorsítótár-bejegyzés még használja. Az CancellationToken átadásra kerül MemoryCacheEntryOptions számára, hogy létrehozzon egy gyorsítótár-bejegyzést, amely egy bizonyos idő után lejár. Így Dispose csak akkor hívható meg, ha a gyorsítótár-bejegyzést eltávolították vagy lejárt. A példakód meghívja a RegisterPostEvictionCallback metódust, hogy regisztráljon egy visszahívást, amely a gyorsítótár-bejegyzés kiürítésekor fog végrehajtódni, és ebben a visszahívásban felszabadítja a CancellationTokenSource-t.

  • A legtöbb alkalmazás IMemoryCache esetében engedélyezve van. Például a AddMvc, AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEngine és sok más Add{Service} metódus hívása a ConfigureServices-ben lehetővé teszi a IMemoryCache-t. Az olyan alkalmazások esetében, amelyek nem az előző Add{Service} módszerek egyikét hívják meg, előfordulhat, hogy szükség lehet a AddMemoryCache behívására ConfigureServices.

Háttér gyorsítótár frissítése

Használjon háttérszolgáltatást , például IHostedService frissítse a gyorsítótárat. A háttérszolgáltatás újrafordíthatja a bejegyzéseket, és csak akkor rendelheti hozzá őket a gyorsítótárhoz, ha készen állnak.

További erőforrások