Ukládání do mezipaměti v paměti v ASP.NET Core

Autor: Rick Anderson, John Luo a Steve Smith

Ukládání do mezipaměti může výrazně zlepšit výkon a škálovatelnost aplikace snížením práce potřebné k vygenerování obsahu. Ukládání do mezipaměti funguje nejlépe s daty, která se mění zřídka a jejich generování je nákladné. Ukládání do mezipaměti vytvoří kopii dat, která se dají vrátit mnohem rychleji než ze zdroje. Aplikace by se měly zapisovat a testovat tak, aby nikdy nezávisely na datech uložených v mezipaměti.

ASP.NET Core podporuje několik různých mezipamětí. Nejjednodušší mezipaměť je založena na IMemoryCache. IMemoryCache představuje mezipaměť uloženou v paměti webového serveru. Aplikace spuštěné na serverové farmě (více serverů) by měly zajistit, aby relace byly při použití mezipaměti v paměti rychlé. Rychlé relace zajišťují, aby požadavky od klienta přešly na stejný server. Webové aplikace Azure například používají směrování žádostí o aplikace (ARR) k směrování všech požadavků na stejný server.

Nelepivé relace ve webové farmě vyžadují distribuovanou mezipaměť , aby nedocházelo k problémům s konzistencí mezipaměti. U některých aplikací může distribuovaná mezipaměť podporovat horizontální navýšení kapacity než mezipaměť v paměti. Použití distribuované mezipaměti přesměruje paměť mezipaměti do externího procesu.

Mezipaměť v paměti může ukládat libovolný objekt. Rozhraní distribuované mezipaměti je omezeno na byte[]. Položky mezipaměti v paměti a distribuované mezipaměti ukládají jako páry klíč-hodnota.

System.runtime. Ukládání do mezipaměti/MemoryCache

System.Runtime.Caching/MemoryCache (balíček NuGet) lze použít s:

  • .NET Standard 2.0 nebo novější.
  • Jakákoli implementace .NET, která cílí na .NET Standard 2.0 nebo novější. Například ASP.NET Core 3.1 nebo novější.
  • .NET Framework 4.5 nebo novější.

Microsoft.Extensions. Ukládání do mezipaměti. Paměť/IMemoryCache (popsaná v tomto článku) se doporučuje, System.Runtime.Caching/MemoryCache protože je lépe integrovaná do ASP.NET Core. Například IMemoryCache nativně funguje s injektáží závislostí ASP.NET Core.

Při přenosu kódu z ASP.NET 4.x na ASP.NET Core se používá System.Runtime.Caching/MemoryCache jako most pro kompatibilitu.

Pokyny pro mezipaměť

  • Kód by měl mít vždy záložní možnost načítání dat, a nezávisí na dostupné hodnotě v mezipaměti.
  • Mezipaměť používá málo prostředků, paměť. Omezení růstu mezipaměti:
    • Nevkládejte do mezipaměti externí vstup. Například použití libovolného uživatelského vstupu jako klíče mezipaměti se nedoporučuje, protože vstup může spotřebovávat nepředvídatelné množství paměti.
    • K omezení růstu mezipaměti použijte vypršení platnosti.
    • K omezení velikosti mezipaměti použijte SetSize, Size a SizeLimit. Modul runtime ASP.NET Core neomezuje velikost mezipaměti na základě zatížení paměti. Záleží na vývojáři, aby omezil velikost mezipaměti.

Použití IMemoryCache

Upozorňující

Použití sdílené mezipaměti paměti z injektáže závislostí a volání SetSize, Sizenebo SizeLimit omezení velikosti mezipaměti může způsobit selhání aplikace. Pokud je v mezipaměti nastavený limit velikosti, musí se při přidání zadat velikost všech položek. To může vést k problémům, protože vývojáři nemusí mít úplnou kontrolu nad tím, co používá sdílenou mezipaměť. Při použití nebo SetSizeSizeSizeLimit omezení mezipaměti vytvořte jednoúčelovou mezipaměť pro ukládání do mezipaměti. Další informace a příklad najdete v tématu Použití setSize, Size a SizeLimit k omezení velikosti mezipaměti. Sdílená mezipaměť je sdílená jednou sdílenou jinými architekturami nebo knihovnami.

Ukládání do mezipaměti v paměti je služba odkazovaná z aplikace pomocí injektáže závislostí. IMemoryCache Požádejte instanci v konstruktoru:

public class IndexModel : PageModel
{
    private readonly IMemoryCache _memoryCache;

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

    // ...

Následující kód používá TryGetValue ke kontrole, jestli je čas v mezipaměti. Pokud čas není uložen v mezipaměti, vytvoří se nová položka a přidá se do mezipaměti pomocí Set:

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

V předchozím kódu je položka mezipaměti nakonfigurována s posuvným vypršením platnosti 3 sekundy. Pokud položka mezipaměti není přístupná déle než tři sekundy, vyřadí se z mezipaměti. Při každém přístupu k položce mezipaměti zůstane v mezipaměti dalších 3 sekund. Třída CacheKeys je součástí ukázky stahování.

Zobrazí se aktuální čas a čas uložený v mezipaměti:

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

Následující kód používá metodu Set rozšíření k ukládání dat do mezipaměti po relativní dobu bez MemoryCacheEntryOptions:

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

V předchozím kódu je položka mezipaměti nakonfigurovaná s relativním vypršením platnosti jednoho dne. Položka mezipaměti se po jednom dni vyřadí z mezipaměti, i když se k ní během tohoto časového limitu přistupuje.

Následující kód používá GetOrCreate a GetOrCreateAsync ukládá data do mezipaměti.

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

    // ...
}

Následující volání Get kódu pro načtení času uloženého v mezipaměti:

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

Následující kód získá nebo vytvoří položku uloženou v mezipaměti s absolutním vypršením platnosti:

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

Sada položek uložených v mezipaměti s pouze posuvným vypršením platnosti je ohrožena tím, že nikdy nevypršela platnost. Pokud je položka uložená v mezipaměti opakovaně přístupná v intervalu klouzavého vypršení platnosti, položka nikdy nevyprší. Zkombinujte posuvné vypršení platnosti s absolutním vypršením platnosti, abyste zajistili vypršení platnosti položky. Absolutní vypršení platnosti nastaví horní mez toho, jak dlouho může být položka uložena do mezipaměti, a přesto umožňuje vypršení platnosti položky dříve, pokud není požadována v intervalu klouzavého vypršení platnosti. Pokud se jedná o posunutý interval vypršení platnosti nebo absolutní čas vypršení platnosti, položka se z mezipaměti vyřadí.

Následující kód získá nebo vytvoří položku uloženou v mezipaměti s posuvným i absolutním vypršením platnosti:

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

Předchozí kód zaručuje, že data nebudou uložena do mezipaměti déle než absolutní čas.

GetOrCreate, GetOrCreateAsynca Get jsou rozšiřující metody ve CacheExtensions třídě. Tyto metody rozšiřují schopnost .IMemoryCache

MemoryCacheEntryOptions

Následující příklad:

  • Nastaví prioritu mezipaměti na CacheItemPriority.NeverRemovehodnotu .
  • PostEvictionDelegate Nastaví, který se zavolá po vyřazení položky z mezipaměti. Zpětné volání se spouští v jiném vlákně než kód, který odebere položku z mezipaměti.
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}.");
}

Omezení velikosti mezipaměti pomocí setSize, Size a SizeLimit

Instance MemoryCache může volitelně zadat a vynutit limit velikosti. Limit velikosti mezipaměti nemá definovanou měrnou jednotku, protože mezipaměť nemá žádný mechanismus pro měření velikosti položek. Pokud je nastavený limit velikosti mezipaměti, musí být zadána velikost všech položek. Modul runtime ASP.NET Core neomezuje velikost mezipaměti na základě zatížení paměti. Záleží na vývojáři, aby omezil velikost mezipaměti. Zadaná velikost je v jednotkách, které vývojář zvolí.

Příklad:

  • Pokud byla webová aplikace primárně ukládáním řetězců do mezipaměti, může mít každá velikost položky mezipaměti délku řetězce.
  • Aplikace může zadat velikost všech položek jako 1 a limit velikosti je počet položek.

Pokud SizeLimit není nastavená, mezipaměť se rozrůstá bez vazby. Modul runtime ASP.NET Core neořízá mezipaměť, pokud je systémová paměť nízká. Aplikace musí být navrženy tak, aby:

  • Omezte růst mezipaměti.
  • Volání Compact nebo Remove pokud je dostupná paměť omezená.

Následující kód vytvoří bezstavovou pevnou velikost MemoryCache přístupnou prostřednictvím injektáže závislostí:

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

SizeLimit nemá jednotky. Položky uložené v mezipaměti musí určovat velikost v jednotkách, které považují za nejvhodnější, pokud je nastavený limit velikosti mezipaměti. Všichni uživatelé instance mezipaměti by měli používat stejný systém jednotek. Položka nebude uložena do mezipaměti, pokud součet velikostí položek uložených v mezipaměti překročí hodnotu zadanou SizeLimithodnotou . Pokud není nastaven žádný limit velikosti mezipaměti, velikost mezipaměti nastavená u položky se ignoruje.

Následující kód se zaregistruje MyMemoryCache v kontejneru injektáže závislostí:

var builder = WebApplication.CreateBuilder(args);

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

MyMemoryCache je vytvořen jako nezávislá mezipaměť paměti pro komponenty, které jsou si vědomi této velikosti omezené mezipaměti a vědět, jak správně nastavit velikost položky mezipaměti.

Velikost položky mezipaměti lze nastavit pomocí SetSize metody rozšíření nebo Size vlastnosti:

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

V předchozím kódu dva zvýrazněné řádky dosáhnou stejného výsledku nastavení velikosti položky mezipaměti. SetSize je k dispozici pro usnadnění při řetězení volání na new MemoryCacheOptions().

MemoryCache.Compact

MemoryCache.Compact pokusí odebrat zadané procento mezipaměti v následujícím pořadí:

  • Všechny položky s vypršenou platností
  • Položky podle priority. Položky s nejnižší prioritou se odeberou jako první.
  • Nejméně naposledy použité objekty.
  • Položky s nejstarším absolutním vypršením platnosti
  • Položky s nejstarším posuvným vypršením platnosti

Připnuté položky s prioritou NeverRemove se nikdy neodeberou. Následující kód odebere položku mezipaměti a volání Compact , která odeberou 25 % položek uložených v mezipaměti:

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

Další informace najdete ve zdroji Compact na GitHubu.

Závislosti mezipaměti

Následující ukázka ukazuje, jak ukončit platnost položky mezipaměti, pokud vyprší platnost závislé položky. Do položky v mezipaměti se přidá A CancellationChangeToken . Při Cancel zavolání se CancellationTokenSourcevyřadí obě položky mezipamě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();
}

CancellationTokenSource Použití možnosti umožňuje vyřazení více položek mezipaměti jako skupiny. using Se vzorem ve výše uvedeném kódu dědí položky mezipaměti vytvořené uvnitř using oboru aktivační události a nastavení vypršení platnosti.

Další poznámky

  • Vypršení platnosti se nenachází na pozadí. Neexistuje žádný časovač, který aktivně prohledává mezipaměť pro položky s vypršenou platností. Jakákoli aktivita v mezipaměti (Get, Set, Remove) může aktivovat vyhledávání položek, jejichž platnost vypršela. Časovač (CancellationTokenSourceCancelAfter) také odebere položku a aktivuje vyhledávání položek, jejichž platnost vypršela. Následující příklad se používá CancellationTokenSource(TimeSpan) pro zaregistrovaný token. Když se tento token aktivuje, okamžitě odebere položku a aktivuje zpětná volání vyřazení:

    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);
    }
    
  • Při použití zpětného volání k opětovnému naplnění položky mezipaměti:

    • Hodnota klíče uložené v mezipaměti může být prázdná, protože zpětné volání se nedokončilo.
    • Výsledkem může být opětovné naplnění položky v mezipaměti několika vlákny.
  • Když se jedna položka mezipaměti použije k vytvoření jiné, podřízená položka zkopíruje tokeny vypršení platnosti nadřazené položky a nastavení vypršení platnosti na základě času. Platnost podřízeného objektu nevypršela ručním odebráním nebo aktualizací nadřazené položky.

  • Slouží PostEvictionCallbacks k nastavení zpětných volání, které se aktivují po vyřazení položky mezipaměti z mezipaměti.

  • U většiny aplikací IMemoryCache je povolená. Například volání AddMvc, , AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEnginea mnoho dalších Add{Service} metod v Program.cs, povolí IMemoryCache. U aplikací, které nevolají jednu z předchozích Add{Service} metod, může být nutné volat AddMemoryCache Program.cs.

Aktualizace mezipaměti na pozadí

K aktualizaci mezipaměti použijte službu IHostedService na pozadí. Služba na pozadí může položky překompilovat a pak je přiřadit k mezipaměti jenom tehdy, když jsou připravené.

Další materiály

Zobrazení nebo stažení ukázkového kódu (postup stažení)

základy Ukládání do mezipaměti

Ukládání do mezipaměti může výrazně zlepšit výkon a škálovatelnost aplikace snížením práce potřebné k vygenerování obsahu. Ukládání do mezipaměti funguje nejlépe s daty, která se mění zřídka a jejich generování je nákladné. Ukládání do mezipaměti vytvoří kopii dat, která se dají vrátit mnohem rychleji než ze zdroje. Aplikace by se měly zapisovat a testovat tak, aby nikdy nezávisely na datech uložených v mezipaměti.

ASP.NET Core podporuje několik různých mezipamětí. Nejjednodušší mezipaměť je založena na IMemoryCache. IMemoryCache představuje mezipaměť uloženou v paměti webového serveru. Aplikace spuštěné na serverové farmě (více serverů) by měly zajistit, aby relace byly při použití mezipaměti v paměti rychlé. Rychlé relace zajišťují, aby následné požadavky klienta přešly na stejný server. Webové aplikace Azure například používají směrování žádostí o aplikace (ARR) k směrování všech následných požadavků na stejný server.

Nelepivé relace ve webové farmě vyžadují distribuovanou mezipaměť , aby nedocházelo k problémům s konzistencí mezipaměti. U některých aplikací může distribuovaná mezipaměť podporovat horizontální navýšení kapacity než mezipaměť v paměti. Použití distribuované mezipaměti přesměruje paměť mezipaměti do externího procesu.

Mezipaměť v paměti může ukládat libovolný objekt. Rozhraní distribuované mezipaměti je omezeno na byte[]. Položky mezipaměti v paměti a distribuované mezipaměti ukládají jako páry klíč-hodnota.

System.runtime. Ukládání do mezipaměti/MemoryCache

System.Runtime.Caching/MemoryCache (balíček NuGet) lze použít s:

  • .NET Standard 2.0 nebo novější.
  • Jakákoli implementace .NET, která cílí na .NET Standard 2.0 nebo novější. Například ASP.NET Core 3.1 nebo novější.
  • .NET Framework 4.5 nebo novější.

Microsoft.Extensions. Ukládání do mezipaměti. Paměť/IMemoryCache (popsaná v tomto článku) se doporučuje, System.Runtime.Caching/MemoryCache protože je lépe integrovaná do ASP.NET Core. Například IMemoryCache nativně funguje s injektáží závislostí ASP.NET Core.

Při přenosu kódu z ASP.NET 4.x na ASP.NET Core se používá System.Runtime.Caching/MemoryCache jako most pro kompatibilitu.

Pokyny pro mezipaměť

  • Kód by měl mít vždy záložní možnost načítání dat, a nezávisí na dostupné hodnotě v mezipaměti.
  • Mezipaměť používá málo prostředků, paměť. Omezení růstu mezipaměti:
    • Nepoužívejte externí vstup jako klíče mezipaměti.
    • K omezení růstu mezipaměti použijte vypršení platnosti.
    • K omezení velikosti mezipaměti použijte SetSize, Size a SizeLimit. Modul runtime ASP.NET Core neomezuje velikost mezipaměti na základě zatížení paměti. Záleží na vývojáři, aby omezil velikost mezipaměti.

Použití IMemoryCache

Upozorňující

Použití sdílené mezipaměti paměti z injektáže závislostí a volání SetSize, Sizenebo SizeLimit omezení velikosti mezipaměti může způsobit selhání aplikace. Pokud je v mezipaměti nastavený limit velikosti, musí se při přidání zadat velikost všech položek. To může vést k problémům, protože vývojáři nemusí mít úplnou kontrolu nad tím, co používá sdílenou mezipaměť. Při použití nebo SetSizeSizeSizeLimit omezení mezipaměti vytvořte jednoúčelovou mezipaměť pro ukládání do mezipaměti. Další informace a příklad najdete v tématu Použití setSize, Size a SizeLimit k omezení velikosti mezipaměti. Sdílená mezipaměť je sdílená jednou sdílenou jinými architekturami nebo knihovnami.

Ukládání do mezipaměti v paměti je služba odkazovaná z aplikace pomocí injektáže závislostí. IMemoryCache Požádejte instanci v konstruktoru:

public class HomeController : Controller
{
    private IMemoryCache _cache;

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

Následující kód používá TryGetValue ke kontrole, jestli je čas v mezipaměti. Pokud čas není uložen v mezipaměti, vytvoří se nová položka a přidá se do mezipaměti pomocí Set. Třída CacheKeys je součástí ukázky stahování.

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

Zobrazí se aktuální čas a čas uložený v mezipaměti:

@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>

Následující kód používá metodu Set rozšíření k ukládání dat do mezipaměti po relativní dobu bez vytvoření objektu MemoryCacheEntryOptions :

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

Hodnota uložená DateTime v mezipaměti zůstane v mezipaměti, zatímco v časovém limitu jsou požadavky.

Následující kód používá GetOrCreate a GetOrCreateAsync ukládá data do mezipaměti.

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

Následující volání Get kódu pro načtení času uloženého v mezipaměti:

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

Následující kód získá nebo vytvoří položku uloženou v mezipaměti s absolutním vypršením platnosti:

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

    return View("Cache", cacheEntry);
}

Sada položek uložených v mezipaměti s pouze posuvným vypršením platnosti je ohrožena tím, že nikdy nevypršela platnost. Pokud je položka uložená v mezipaměti opakovaně přístupná v intervalu klouzavého vypršení platnosti, položka nikdy nevyprší. Zkombinujte posuvné vypršení platnosti s absolutním vypršením platnosti, abyste zajistili vypršení platnosti položky. Absolutní vypršení platnosti nastaví horní mez toho, jak dlouho může být položka uložena do mezipaměti, a přesto umožňuje vypršení platnosti položky dříve, pokud není požadována v intervalu klouzavého vypršení platnosti. Pokud se jedná o posunutý interval vypršení platnosti nebo absolutní čas vypršení platnosti, položka se z mezipaměti vyřadí.

Následující kód získá nebo vytvoří položku uloženou v mezipaměti s posuvným i absolutním vypršením platnosti:

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

Předchozí kód zaručuje, že data nebudou uložena do mezipaměti déle než absolutní čas.

GetOrCreate, GetOrCreateAsynca Get jsou rozšiřující metody ve CacheExtensions třídě. Tyto metody rozšiřují schopnost .IMemoryCache

MemoryCacheEntryOptions

Následující ukázka:

  • Nastaví klouzavý čas vypršení platnosti. Žádosti o přístup k této položce v mezipaměti resetuje posuvné hodiny vypršení platnosti.
  • Nastaví prioritu mezipaměti na CacheItemPriority.NeverRemovehodnotu .
  • PostEvictionDelegate Nastaví, že bude volána po vyřazení položky z mezipaměti. Zpětné volání se spouští v jiném vlákně než kód, který odebere položku z mezipaměti.
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);
}

Omezení velikosti mezipaměti pomocí setSize, Size a SizeLimit

Instance MemoryCache může volitelně zadat a vynutit limit velikosti. Limit velikosti mezipaměti nemá definovanou měrnou jednotku, protože mezipaměť nemá žádný mechanismus měření velikosti položek. Pokud je nastavený limit velikosti mezipaměti, musí být zadána velikost všech položek. Modul runtime ASP.NET Core neomezuje velikost mezipaměti na základě zatížení paměti. Záleží na vývojáři, aby omezil velikost mezipaměti. Zadaná velikost je v jednotkách, které vývojář zvolí.

Příklad:

  • Pokud byla webová aplikace primárně ukládáním řetězců do mezipaměti, může mít každá velikost položky mezipaměti délku řetězce.
  • Aplikace může zadat velikost všech položek jako 1 a limit velikosti je počet položek.

Pokud SizeLimit není nastavená, mezipaměť se rozrůstá bez vazby. Modul runtime ASP.NET Core neořízá mezipaměť, pokud je systémová paměť nízká. Aplikace musí být navrženy tak, aby:

  • Omezte růst mezipaměti.
  • Volání Compact nebo Remove pokud je dostupná paměť omezená:

Následující kód vytvoří bezstavovou pevnou velikost MemoryCache přístupnou prostřednictvím injektáže závislostí:

// 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á jednotky. Položky uložené v mezipaměti musí určovat velikost v jednotkách, které považují za nejvhodnější, pokud je nastaven limit velikosti mezipaměti. Všichni uživatelé instance mezipaměti by měli používat stejný systém jednotek. Položka nebude uložena do mezipaměti, pokud součet velikostí položek uložených v mezipaměti překročí hodnotu zadanou SizeLimithodnotou . Pokud není nastavený žádný limit velikosti mezipaměti, bude velikost mezipaměti nastavená u položky ignorována.

Následující kód zaregistruje MyMemoryCache kontejner injektáž závislostí.

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

MyMemoryCache je vytvořen jako nezávislá mezipaměť paměti pro komponenty, které jsou si vědomi této velikosti omezené mezipaměti a vědět, jak správně nastavit velikost položky mezipaměti.

Následující kód používá MyMemoryCache:

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

Velikost položky mezipaměti lze nastavit pomocí Size SetSize metod rozšíření:

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 pokusí odebrat zadané procento mezipaměti v následujícím pořadí:

  • Všechny položky s vypršenou platností
  • Položky podle priority. Položky s nejnižší prioritou se odeberou jako první.
  • Nejméně naposledy použité objekty.
  • Položky s nejstarším absolutním vypršením platnosti
  • Položky s nejstarším posuvným vypršením platnosti

Připnuté položky s prioritou NeverRemove se nikdy neodeberou. Následující kód odebere položku mezipaměti a volání Compact:

_cache.Remove(MyKey);

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

Další informace najdete ve zdroji Compact na GitHubu.

Závislosti mezipaměti

Následující ukázka ukazuje, jak ukončit platnost položky mezipaměti, pokud vyprší platnost závislé položky. Do položky v mezipaměti se přidá A CancellationChangeToken . Při Cancel zavolání v CancellationTokenSourcemezipaměti se vyřadí obě položky mezipaměti.

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 Použití možnosti umožňuje vyřazení více položek mezipaměti jako skupiny. using Při použití vzoru v kódu výše se položky mezipaměti vytvořené uvnitř using bloku dědí triggery a nastavení vypršení platnosti.

Další poznámky

  • Vypršení platnosti se nenachází na pozadí. Neexistuje žádný časovač, který aktivně prohledává mezipaměť pro položky s vypršenou platností. Jakákoli aktivita v mezipaměti (Get, Set, Remove) může aktivovat vyhledávání položek, jejichž platnost vypršela. Časovač (CancellationTokenSourceCancelAfter) také odebere položku a aktivuje vyhledávání položek, jejichž platnost vypršela. Následující příklad se používá CancellationTokenSource(TimeSpan) pro zaregistrovaný token. Když se tento token aktivuje, okamžitě odebere položku a aktivuje zpětná volání vyřazení:

    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);
    }
    
  • Při použití zpětného volání k opětovnému naplnění položky mezipaměti:

    • Hodnota klíče uložené v mezipaměti může být prázdná, protože zpětné volání se nedokončilo.
    • Výsledkem může být opětovné naplnění položky v mezipaměti několika vlákny.
  • Když se jedna položka mezipaměti použije k vytvoření jiné, podřízená položka zkopíruje tokeny vypršení platnosti nadřazené položky a nastavení vypršení platnosti na základě času. Platnost podřízeného objektu nevypršela ručním odebráním nebo aktualizací nadřazené položky.

  • Slouží PostEvictionCallbacks k nastavení zpětných volání, které se aktivují po vyřazení položky mezipaměti z mezipaměti. V ukázkovém kódu je volána k CancellationTokenSource.Dispose() uvolnění nespravovaných prostředků používaných nástrojem CancellationTokenSource. Tato položka CancellationTokenSource však není okamžitě odstraněna, protože je stále používána položkou mezipaměti. Předá CancellationToken se k MemoryCacheEntryOptions vytvoření položky mezipaměti, která vyprší po určité době. Proto Dispose by se nemělo volat, dokud se neodebere položka mezipaměti nebo nevypršela jeho platnost. Ukázkový kód volá metodu RegisterPostEvictionCallback pro registraci zpětného volání, která bude vyvolána při vyřazení položky mezipaměti a odstraní v tomto zpětném CancellationTokenSource volání.

  • U většiny aplikací IMemoryCache je povolená. Například volání AddMvc, , AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEnginea mnoho dalších Add{Service} metod v ConfigureServices, povolí IMemoryCache. U aplikací, které nevolají jednu z předchozích Add{Service} metod, může být nutné volat AddMemoryCache ConfigureServices.

Aktualizace mezipaměti na pozadí

K aktualizaci mezipaměti použijte službu IHostedService na pozadí. Služba na pozadí může položky překompilovat a pak je přiřadit k mezipaměti jenom tehdy, když jsou připravené.

Další materiály