ASP.NET Core 'de önbellek belleği

By Rick Anderson, John Luove Steve Smith

Örnek kodu görüntüleme veya indirme (nasıl indirileceği)

Önbelleğe Alma temel bilgiler

Önbelleğe Alma, içerik oluşturmak için gereken işi azaltarak bir uygulamanın performansını ve ölçeklenebilirliğini önemli ölçüde iyileştirebilir. Önbelleğe Alma, seyrek olarak değişen ve üretime masraflı verilerle en iyi şekilde çalışıyor. Önbelleğe Alma, kaynaktan çok daha hızlı döndürülebilecek verilerin bir kopyasını oluşturur. Uygulamalar, önbelleğe alınmış verilere hiçbir şekilde bağlı olmayacak şekilde yazılmalıdır ve test edilmelidir.

ASP.NET Core birçok farklı önbelleği destekler. En basit önbellek ımemorycache' i temel alır. IMemoryCache Web sunucusunun belleğinde depolanan bir önbelleği temsil eder. Sunucu grubunda (birden çok sunucu) çalışan uygulamalar, bellek içi önbellek kullanılırken oturumların yapışkan olmasını sağlamalıdır. Yapışkan oturumlar, bir istemciden gelen sonraki isteklerin aynı sunucuya gitmesini sağlar. Örneğin, Azure Web Apps, sonraki tüm istekleri aynı sunucuya yönlendirmek için uygulama Isteği yönlendirme (ARR) kullanır.

Bir Web grubundaki yapışkan olmayan oturumlar, önbellek tutarlılığı sorunlarından kaçınmak için Dağıtılmış bir önbellek gerektirir. Bazı uygulamalarda, dağıtılmış bir önbellek, bellek içi bir önbellekten daha yüksek genişleme desteği sağlayabilir. Dağıtılmış bir önbellek kullanmak önbellek belleğini bir dış işleme devreder.

Bellek içi önbellek herhangi bir nesneyi depolayabilirler. Dağıtılmış önbellek arabirimi ile sınırlıdır byte[] . Bellek içi ve dağıtılmış önbellek deposu öğeleri anahtar-değer çiftleri olarak önbelleğe alma.

System. Runtime. Önbelleğe Alma/memorycache

System.Runtime.Caching/MemoryCache(NuGet package) ile birlikte kullanılabilir:

  • .NET Standard 2,0 veya üzeri.
  • .NET Standard 2,0 veya sonraki bir sürümü hedefleyen tüm .NET uygulamaları . örneğin, 3,1 veya üzeri ASP.NET Core.
  • .NET Framework 4,5 veya üzeri.

Microsoft. Extensions. Önbelleğe Alma. / IMemoryCache System.Runtime.Caching / MemoryCache ASP.NET Core ' ye daha iyi tümleştirildiği için bellek (bu makalede açıklanan) önerilir. örneğin, IMemoryCache ASP.NET Core bağımlılığı eklemeile yerel olarak işe yarar.

System.Runtime.Caching / MemoryCache ASP.NET 4. x öğesinden ASP.NET Core kod taşıma sırasında bir uyumluluk köprüsü olarak kullanın.

Önbellek yönergeleri

  • Kodun, verileri getirmek için her zaman bir geri dönüş seçeneği olmalıdır ve kullanılabilir önbelleğe alınmış bir değere bağlı değildir .
  • Önbellek bir nadir kaynağı, bellek kullanır. Önbellek büyümesini sınırla:

Imemorycache kullan

Uyarı

Bağımlılık ekleme ve arama SetSize , ya da önbellek boyutunu sınırlamak için paylaşılan bellek önbelleğinin kullanılması Size SizeLimit uygulamanın başarısız olmasına neden olabilir. Önbellekte bir boyut sınırı ayarlandığında, tüm girişlerin eklenmekte olan bir boyut belirtmesi gerekir. Bu, geliştiricilerin paylaşılan önbelleğin kullanıldığı ilgili tam denetime sahip olmaması nedeniyle sorunlara yol açabilir. ,, SetSize Size Veya SizeLimit önbelleğini sınırlandırmak için, önbelleğe alma için tek bir önbellek oluşturun. Daha fazla bilgi ve bir örnek için bkz. önbellek boyutunu sınırlamak Için SetSize, size ve SizeLimit kullanma. Paylaşılan bir önbellek, diğer çerçeveler veya kitaplıklar tarafından paylaşılır.

Bellek içi önbelleğe alma, bağımlılık eklemekullanılarak bir uygulamadan başvurulan bir hizmettir . IMemoryCacheÖrneği oluşturucuda iste:

public class HomeController : Controller
{
    private IMemoryCache _cache;

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

Aşağıdaki kod, bir saatin önbellekte olup olmadığını denetlemek için TryGetValue kullanır. Bir zaman önbelleğe alınmadıysa, yeni bir giriş oluşturulur ve Ayarlabirlikte önbelleğe eklenir. CacheKeysSınıf, indirme örneğinin bir parçasıdır.

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

Geçerli saat ve önbelleğe alınmış saat görüntülenir:

@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şağıdaki kod, nesneyi oluşturmadan göreli bir süre için verileri önbelleğe almak üzere set genişletme yöntemini kullanır 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);
}

DateTimeZaman aşımı süresi içinde istekler varken önbelleğe alınan değer önbellekte kalır.

Aşağıdaki kod, verileri önbelleğe almak için GetOrCreate ve Getorcreateasync kullanır.

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

Aşağıdaki kod, önbelleğe alınmış zamanı getirmek için Al yöntemini çağırır:

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

Aşağıdaki kod, mutlak süre sonu ile önbelleğe alınmış bir öğe alır veya oluşturur:

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

    return View("Cache", cacheEntry);
}

Yalnızca kayan bir süre sonu olan önbelleğe alınmış bir öğe kümesi, eski olma riski altında. Kayan süre sonu aralığından daha sık erişiliyorsa, öğe hiçbir zaman sona ermez. Mutlak süre sonu zamanı başarılı olduktan sonra öğenin süresinin dolacağını garantilemek için kayan bir süre sonu mutlak bir süre sonu ile birleştirin. Mutlak süre sonu, öğenin, Kayan süre sonu aralığı içinde istenmediğinde daha önce süresinin dolmasına izin verirken öğenin ne kadar süre önbellekte önbelleğe alınacağını belirleyen bir üst sınır ayarlar. Mutlak ve kayan süre sonu belirtildiğinde, süre sonları mantıksal ORed. Kayan süre sonu aralığı veya mutlak süre sonu zamanı başarılı olursa, öğe önbellekten çıkarıldı.

Aşağıdaki kod hem kayan hem de mutlak süre sonu ile önbelleğe alınmış bir öğe alır veya oluşturur:

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

Yukarıdaki kod, verilerin mutlak süreden daha uzun süre önbelleğe alınmamasını garanti eder.

GetOrCreate, GetOrCreateAsync ve, Get sınıfında uzantı yöntemleridir CacheExtensions . Bu yöntemler kapasitesini genişletir IMemoryCache .

MemoryCacheEntryOptions

Aşağıdaki örnek:

  • Kayan süre sonu zamanı ayarlar. Bu önbelleğe alınmış öğeye erişen istekler, Kayan süre sonu saatini sıfırlayacaktır.
  • Önbellek önceliğini Cacheitempriınıd. NeverRemoveolarak ayarlar.
  • Giriş önbellekten çıkarıldıktan sonra çağrılacak Postevictiondelegate ayarlar. Geri çağırma, öğeyi önbellekten kaldıran koddan farklı bir iş parçacığında çalıştırılır.
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);
}

Önbellek boyutunu sınırlamak için SetSize, size ve SizeLimit kullanın

MemoryCacheÖrnek, isteğe bağlı olarak bir boyut sınırı belirtebilir ve uygulayabilir. Önbelleğin, girdilerin boyutunu ölçmeye yönelik bir mekanizması olmadığından, önbellek boyutu sınırının tanımlı bir ölçü birimi yok. Önbellek boyutu sınırı ayarlandıysa, tüm girişlerin boyut belirtmesi gerekir. ASP.NET Core çalışma zamanı, bellek baskısı temelinde önbellek boyutunu sınırlamaz. En fazla geliştirici, önbellek boyutunu sınırlayacak. Belirtilen boyut, geliştiricinin seçtiği birimlerde bulunur.

Örnek:

  • Web uygulaması öncelikle dizeleri önbelleğe alıyorsa, her önbellek girdisi boyutu dize uzunluğu olabilir.
  • Uygulama tüm girdilerin boyutunu 1 olarak belirtebilir ve boyut sınırı girdi sayısıdır.

SizeLimitAyarlanmamışsa, önbellek bağlantılı olmadan büyür. ASP.NET Core çalışma zamanı, sistem belleği azaldığında önbelleği kırpmaz. Uygulamalar şu şekilde tasarlanmış olmalıdır:

  • Önbellek büyümesini sınırla.
  • CompactKullanılabilir bellek için arama veya Remove zaman sınırlı olduğunda:

Aşağıdaki kod, MemoryCache bağımlılık eklemetarafından erişilebilen bir unitless sabit boyutu oluşturur:

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

SizeLimit birimleri yok. Önbellek boyutu sınırı ayarlandıysa, önbelleğe alınmış girişler, en çok ne kadar uygun olduğunu belirleyen birimlerde boyut belirtmelidir. Bir önbellek örneğinin tüm kullanıcıları aynı birim sistemini kullanmalıdır. Önbelleğe alınmış giriş boyutlarının toplamı tarafından belirtilen değeri aşarsa, bir giriş önbelleğe alınmaz SizeLimit . Önbellek boyutu sınırı ayarlanmamışsa, girişte ayarlanan önbellek boyutu yok sayılır.

Aşağıdaki kod, MyMemoryCache bağımlılık ekleme kapsayıcısına kaydolur.

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

MyMemoryCache , bu boyut sınırlı önbelleğin farkında olan bileşenler için bağımsız bir bellek önbelleği olarak oluşturulur ve önbellek girişi boyutunu uygun şekilde ayarlamayı öğrenin.

Aşağıdaki kod şunu kullanır 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");
    }
}

Önbellek girişinin boyutu Size veya SetSize genişletme yöntemleriyle ayarlanabilir:

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 önbelleğin belirtilen yüzdesini aşağıdaki sırayla kaldırmaya çalışır:

  • Süresi dolan tüm öğeler.
  • Önceliğe göre öğeler. Önce en düşük öncelikli öğeler kaldırılır.
  • En son kullanılan nesneler.
  • En erken mutlak süre sonu olan öğeler.
  • En erken kayan süre sonu olan öğeler.

Önceliğe sahip sabitlenmiş NeverRemove öğeler hiçbir zaman kaldırılamaz. Aşağıdaki kod bir önbellek öğesini kaldırır ve öğesini Compact çağırtır:

_cache.Remove(MyKey);

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

Daha fazla bilgi için bkz. GitHub sıkıştırılmış kaynak.

Önbellek bağımlılıkları

Aşağıdaki örnekte, bağımlı bir girişin süresi dolsa önbellek girişinin süresinin nasıl sona erer? Önbelleğe CancellationChangeToken alınan öğeye eklenir. üzerinde Cancel çağrıldı CancellationTokenSource mı, her iki önbellek girdisi de çıkar çıkarıldı.

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

bir CancellationTokenSource kullanmak, birden çok önbellek girdisini grup olarak çıkarmayı sağlar. Yukarıdaki using kodda yer alan desenle, bloğun içinde oluşturulan önbellek using girişleri tetikleyicileri ve süre sonu ayarlarını devralacak.

Ek notlar

  • Süre sonu arka planda olmaz. Önbelleği süresi dolmuş öğeler için etkin olarak tarayan bir zamanlayıcı yoktur. Önbellekte ( Get , Set , ) herhangi bir Remove etkinlik, süresi dolan öğeler için bir arka plan taraması tetikler. üzerinde bir zamanlayıcı CancellationTokenSource ( CancelAfter ) ayrıca girdiyi kaldırır ve süresi dolan öğeler için bir tarama tetikler. Aşağıdaki örnek, kayıtlı belirteç için CancellationTokenSource(TimeSpan) kullanır. Bu belirteç harekete olduğunda girişi hemen kaldırır ve çıkarma geri çağırmalarını harekete alır:
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);
}
  • Önbellek öğesini yeniden doldurmak için geri çağırma kullanırken:

    • Geri çağırma tamamlanmamış olduğundan, birden çok istek önbelleğe alınmış anahtar değerini boş bulabilir.
    • Bu, önbelleğe alınan öğenin yeniden doldurularak birkaç iş parçacığının yeniden doldurularak sonuçlandırabilirsiniz.
  • Bir önbellek girişi başka bir önbellek girişi oluşturmak için kullanılırsa, alt öğe üst girdinin süre sonu belirteçlerini ve zaman tabanlı süre sonu ayarlarını kopyalar. Üst girişin el ile kaldırılması veya güncelleştirilerek alt öğenin süresi dolmaz.

  • Önbellek PostEvictionCallbacks girdisi önbellekten çıkarıldığı zaman çağrılarını ayarlamak için kullanın.

  • Çoğu uygulama için IMemoryCache etkindir. Örneğin , , AddMvc , , ve içinde diğer birçok AddControllersWithViews AddRazorPages AddMvcCore().AddRazorViewEngine Add{Service} yöntemin çağrılarak ConfigureServices özelliğine olanak IMemoryCache sağlar. Önceki yöntemlerden birini çağırmayan Add{Service} uygulamalar için içinde çağrısı yapmak AddMemoryCache ConfigureServices gerekebilir.

Arka plan önbelleği güncelleştirmesi

Önbelleği güncelleştirmek için gibi bir arka plan IHostedService hizmeti kullanın. Arka plan hizmeti girdileri yeniden tamamlar ve sonra bunları yalnızca hazır olduğunda önbelleğe atar.

Ek kaynaklar