ASP.NET Core의 메모리 내 캐싱 소개Introduction to in-memory caching in ASP.NET Core

Rick Anderson, John Luo, 및 Steve SmithBy Rick Anderson, John Luo, and Steve Smith

보거나 다운로드 샘플 코드 (다운로드 하는 방법을)View or download sample code (how to download)

캐싱 기본 사항Caching basics

캐싱 크게 향상 시킬 수 성능 및 확장성 응용 프로그램의 콘텐츠를 생성 하는 데 필요한 작업을 줄여 합니다.Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. 캐싱 동작 자주 변경 되는 데이터에 가장 적합 합니다.Caching works best with data that changes infrequently. 많은 반환 될 수 있는 데이터의 복사본을 만들어 캐시 원본에서 보다 더 빠릅니다.Caching makes a copy of data that can be returned much faster than from the original source. 작성 하 고 캐시 된 데이터에 의존 하지를 응용 프로그램을 테스트 해야 합니다.You should write and test your app to never depend on cached data.

ASP.NET Core 몇 가지 다른 캐시를 지원합니다.ASP.NET Core supports several different caches. 가장 간단한 캐시 기반는 IMemoryCache을 웹 서버의 메모리에 저장 된 캐시를 나타냅니다.The simplest cache is based on the IMemoryCache, which represents a cache stored in the memory of the web server. 여러 서버의 서버 팜에서 실행 되는 응용 프로그램 메모리에 캐시를 사용 하는 경우 세션 스티커 인지 확인 해야 합니다.Apps which run on a server farm of multiple servers should ensure that sessions are sticky when using the in-memory cache. 고정 세션 모든 클라이언트에서 후속 요청이 동일한 서버로 이동 있는지 확인 합니다.Sticky sessions ensure that subsequent requests from a client all go to the same server. 예를 들어 Azure 웹 앱 사용 응용 프로그램 요청 라우팅 (ARR) 동일한 서버에 모든 후속 요청을 라우팅할 수 있습니다.For example, Azure Web apps use Application Request Routing (ARR) to route all subsequent requests to the same server.

웹 팜에서 아닌 고정 세션 필요는 분산 캐시 캐시 일관성 문제가 발생 하지 않도록 합니다.Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. 일부 응용 프로그램에 대 한 분산된 캐시 메모리에 캐시 보다 더 높은 규모 확장을 지원할 수 있습니다.For some apps, a distributed cache can support higher scale out than an in-memory cache. 분산된 캐시를 사용 하 여 외부 프로세스에 캐시 메모리 오프 로드 합니다.Using a distributed cache offloads the cache memory to an external process.

IMemoryCache 하지 않는 한 캐시의 메모리 캐시 항목을 제거 하는 우선 순위를 캐시 로 설정 된 CacheItemPriority.NeverRemove합니다.The IMemoryCache cache will evict cache entries under memory pressure unless the cache priority is set to CacheItemPriority.NeverRemove. 설정할 수 있습니다는 CacheItemPriority 캐시를 우선 순위를 조정 하려면 메모리가 중에서 항목을 제거 합니다.You can set the CacheItemPriority to adjust the priority the cache evicts items under memory pressure.

메모리 내 캐시는 모든 개체를 저장할 수 있습니다. 분산된 캐시 인터페이스는 제한 byte[]합니다.The in-memory cache can store any object; the distributed cache interface is limited to byte[].

IMemoryCache를 사용 하 여Using IMemoryCache

메모리 내 캐시는 서비스 사용 하 여 응용 프로그램에서 참조 되는 종속성 주입합니다.In-memory caching is a service that is referenced from your app using Dependency Injection. 호출 AddMemoryCacheConfigureServices:Call AddMemoryCache in ConfigureServices:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute();
    }
}

요청 된 IMemoryCache 생성자에 대 한 인스턴스:Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

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

IMemoryCacheNuGet 패키지 "Microsoft.Extensions.Caching.Memory" 필요합니다.IMemoryCache requires NuGet package "Microsoft.Extensions.Caching.Memory".

다음 코드에서는 TryGetValue 을 현재 시간 캐시에 있는지 확인 하십시오.The following code uses TryGetValue to check if the current time is in the cache. 새 항목 만들어지고과 함께 캐시에 추가 된 항목, 캐시 되지 않으면 설정합니다.If the item is not cached, a new entry is created and added to the cache with Set.

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

캐시 된 시간과 현재 시간 표시 됩니다.The current time and the cached time is displayed:

@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="CacheGetOrCreateAsync">GetOrCreateAsync</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</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>

캐시 된 DateTime 있을 때는 요청 제한 시간 (및 메모리 부족으로 인해 없는 제거) 내에서 값이 캐시에 유지 됩니다.The cached DateTime value will remain in the cache while there are requests within the timeout period (and no eviction due to memory pressure). 다음 이미지는 현재 시간 및 캐시에서 검색 하는 이전 시간을 보여 줍니다.The image below shows the current time and an older time retrieved from cache:

인덱스 뷰를 표시 하는 두 개의 서로 다른 시간

다음 코드에서는 GetOrCreateGetOrCreateAsync 데이터를 캐시 합니다.The following code uses GetOrCreate and GetOrCreateAsync to cache data.

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> CacheGetOrCreateAsync()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return Task.FromResult(DateTime.Now);
    });

    return View("Cache", cacheEntry);
}

다음 호출 코드 가져오기 를 캐시 된 시간을 가져옵니다.The following code calls Get to fetch the cached time:

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

참조 IMemoryCache 메서드CacheExtensions 메서드 에 대 한 설명은 캐시 메서드.See IMemoryCache methods and CacheExtensions methods for a description of the cache methods.

MemoryCacheEntryOptions를 사용 하 여Using MemoryCacheEntryOptions

다음 샘플:The following sample:

  • 절대 만료 시간을 설정합니다.Sets the absolute expiration time. 이 항목을 캐시 될 수 있는 최대 시간 고 슬라이딩 만료 지속적으로 갱신 되 면 너무 부실 항목 방지 합니다.This is the maximum time the entry can be cached and prevents the item from becoming too stale when the sliding expiration is continuously renewed.
  • 슬라이딩 만료 시간을 설정합니다.Sets a sliding expiration time. 이 캐시 된 항목에 액세스 하는 요청에는 상대 (sliding) 만료 시계가 다시 설정 됩니다.Requests that access this cached item will reset the sliding expiration clock.
  • 캐시 우선 순위 설정 CacheItemPriority.NeverRemove합니다.Sets the cache priority to CacheItemPriority.NeverRemove.
  • 집합은 PostEvictionDelegate 하는 후에 호출 됩니다는 엔트리를 캐시에서 제거 합니다.Sets a PostEvictionDelegate that will be called after the entry is evicted from the cache. 콜백 캐시에서 항목을 제거 하는 코드에서 다른 스레드에서 실행 됩니다.The callback is run on a different thread from the code that removes the item from the cache.
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);
}

캐시 종속성Cache dependencies

다음 예제에는 종속 항목을 만료 하는 경우 캐시 항목을 만료 하는 방법을 보여 줍니다.The following sample shows how to expire a cache entry if a dependent entry expires. A CancellationChangeToken 캐시 된 항목에 추가 됩니다.A CancellationChangeToken is added to the cached item. Cancel 라고 하는 CancellationTokenSource, 캐시 항목이 모두 제거 됩니다.When Cancel is called on the CancellationTokenSource, both cache entries are evicted.

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 여러 캐시 항목이 그룹으로 제거할 수 있습니다.Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. using 내에 만들어진 위의 코드에서 패턴, 캐시 항목의 using 블록 트리거 및 만료 설정을 상속 합니다.With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

추가 참고 사항Additional notes

  • 콜백을 사용 하 여 캐시 항목을 다시 채우기:When using a callback to repopulate a cache item:

    • 여러 개의 요청 찾을 수 있는 캐시 된 키 값 빈 콜백을 완료 되지 않았습니다.Multiple requests can find the cached key value empty because the callback hasn't completed.
    • 이 캐시 된 항목을 채우는 여러 스레드 발생할 수 있습니다.This can result in several threads repopulating the cached item.
  • 하나의 캐시 항목을 사용 하 여 다른 만들을 부모 항목의 만료 토큰 및 시간 기반 만료 설정을 자식 복사 합니다.When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. 자식 수동 제거 하 여 만료 된 또는 부모 항목의 업데이트 아닙니다.The child is not expired by manual removal or updating of the parent entry.

기타 리소스Other Resources