Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Рик Андерсон, Джон Луо и Стив Смит
Кэширование может значительно повысить производительность и масштабируемость приложения, сокращая работу, необходимую для создания содержимого. Кэширование лучше всего работает с данными, которые изменяются редко и являются дорогостоящими для создания. Кэширование создает копию данных, которые могут быть возвращены намного быстрее, чем из источника. Приложения следует разрабатывать и тестировать так, чтобы они никогда не зависели от кэшированных данных.
ASP.NET Core поддерживает несколько разных кэшей. Самый простой кэш основан на IMemoryCache.
IMemoryCache
представляет кэш, хранящийся в памяти веб-сервера. Приложения, работающие на ферме серверов (несколько серверов), должны гарантировать, что сеансы закреплены при использовании кэша в памяти. Привязанные сессии гарантируют, что запросы от клиента отправляются на один и тот же сервер. Например, веб-приложения Azure используют маршрутизацию запросов приложений (ARR) для маршрутизации всех запросов на один и тот же сервер.
Сеансы без привязки в веб-ферме требуют распределенного кэша, чтобы избежать проблем согласованности кэша. Для некоторых приложений распределенный кэш может поддерживать более высокий масштаб, чем кэш в памяти. Использование распределенного кэша отгрузит память кэша во внешний процесс.
Кэш в памяти может хранить любой объект. Интерфейс распределенного кэша ограничен byte[]
. Элементы кэша в памяти и распределенном кэше хранятся в виде пар "ключ-значение".
System.Runtime.Caching/MemoryCache
System.Runtime.Caching / MemoryCache (пакет NuGet) можно использовать с:
- .NET Standard 2.0 или более поздней версии.
- Любая реализация .NET , предназначенная для .NET Standard 2.0 или более поздней версии. Например, ASP.NET Core 3.1 или более поздней версии.
- .NET Framework 4.5 или более поздней версии.
Microsoft.Extensions.Caching.Memory/IMemoryCache
(описана в этой статье) рекомендуется, так как она лучше интегрирована в ASP.NET Core, чем System.Runtime.Caching
/MemoryCache
. Например, IMemoryCache
нативно работает с внедрением зависимостей в ASP.NET Core.
Используйте System.Runtime.Caching
/MemoryCache
в качестве моста совместимости при переносе кода из ASP.NET 4.x в ASP.NET Core.
Рекомендации по кэшированию
- Код всегда должен иметь резервный вариант для получения данных и не зависит от кэшированного значения.
- Кэш использует дефицитный ресурс, память. Ограничение роста кэша:
- Не вставлять внешние входные данные в кэш. Например, использование произвольных пользовательских входных данных в качестве ключа кэша не рекомендуется, так как входные данные могут использовать непредсказуемый объем памяти.
- Используйте срок действия, чтобы ограничить рост кэша.
- Используйте SetSize, Size и SizeLimit, чтобы ограничить размер кэша. Среда выполнения ASP.NET Core не ограничивает размер кэша на основе давления памяти. Разработчик сам решает, каким образом ограничить размер кэша.
Использование IMemoryCache
Предупреждение
Использование общего кэша памяти из службы внедрения зависимостей и использование SetSize
, Size
или SizeLimit
для ограничения размера кэша может привести к сбою приложения. Если для кэша задано ограничение размера, все записи должны указать размер при добавлении. Это может привести к проблемам, так как разработчики могут не иметь полного контроля над тем, что использует общий кэш.
При использовании SetSize
, Size
или SizeLimit
для ограничения кэша создайте синглтон для кэширования. Дополнительные сведения и пример см. в разделе Use SetSize, Size и SizeLimit, чтобы ограничить размер кэша.
Общий кэш — это кэш, которым пользуются другие фреймворки или библиотеки.
Кэширование в памяти — это служба , на которую ссылается приложение с помощью внедрения зависимостей. Запросите экземпляр IMemoryCache
в конструкторе.
public class IndexModel : PageModel
{
private readonly IMemoryCache _memoryCache;
public IndexModel(IMemoryCache memoryCache) =>
_memoryCache = memoryCache;
// ...
Следующий код используется TryGetValue для проверки времени в кэше. Если время не кэшируется, создается новая запись и добавляется в кэш с помощью 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;
}
В приведенном выше коде запись кэша настраивается с скользящим сроком действия в три секунды. Если запись кэша не используется более трёх секунд, она вытесняется из кэша. При каждом доступе к записи кэша он остается в кэше еще на 3 секунды. Класс CacheKeys
является частью образца для скачивания.
Отображается текущее время и кэшированное время:
<ul>
<li>Current Time: @Model.CurrentDateTime</li>
<li>Cached Time: @Model.CacheCurrentDateTime</li>
</ul>
Следующий код использует метод расширения Set
для кэширования данных на определенное время без использования MemoryCacheEntryOptions
.
_memoryCache.Set(CacheKeys.Entry, DateTime.Now, TimeSpan.FromDays(1));
В приведенном выше коде запись кэша настраивается с относительным сроком действия в один день. Запись кэша удаляется из кэша через один день, даже если к ней обращались в течение периода времени ожидания.
Следующий код использует GetOrCreate и GetOrCreateAsync кэширует данные.
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);
});
// ...
}
Следующий код вызывает Get, чтобы получить кэшированное время.
var cacheEntry = _memoryCache.Get<DateTime?>(CacheKeys.Entry);
Следующий код получает или создает кэшированный элемент с абсолютным истечением срока действия:
var cachedValue = _memoryCache.GetOrCreate(
CacheKeys.Entry,
cacheEntry =>
{
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
return DateTime.Now;
});
Кэшированный набор элементов с исключительно скользящим истечением срока действия находится под угрозой никогда не истечь. Если кэшированный элемент неоднократно обращается к скользящему интервалу истечения срока действия, срок действия элемента никогда не истекает. Объедините скользящий срок действия с абсолютным сроком действия, чтобы гарантировать истечение срока действия элемента. Абсолютный срок действия задает верхнюю границу по времени, в течение которого элемент может быть кэширован, но позволяет элементу истекать раньше, если он не запрашивается в течение скользящего интервала. Если проходит либо скользящий интервал истечения срока действия, либо абсолютное время истечения, элемент удаляется из кэша.
Следующий код получает или создает кэшированный элемент с скользящим и абсолютным сроком действия:
var cachedValue = _memoryCache.GetOrCreate(
CacheKeys.CallbackEntry,
cacheEntry =>
{
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
return DateTime.Now;
});
Предыдущий код гарантирует, что данные не будут кэшироваться дольше абсолютного времени.
GetOrCreate, GetOrCreateAsync и Get являются методами расширения в классе CacheExtensions. Эти методы расширяют возможности IMemoryCache.
MemoryCacheEntryOptions
Следующий пример:
- Задает приоритет CacheItemPriority.NeverRemoveкэша.
- Задает объект PostEvictionDelegate , который вызывается после вытеснения записи из кэша. Обратный вызов выполняется в другом потоке кода, который удаляет элемент из кэша.
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}.");
}
Использование SetSize, Size и SizeLimit для ограничения размера кэша
Экземпляр MemoryCache
может по желанию указать и применить ограничение размера. Ограничение размера кэша не имеет определенной единицы измерения, так как кэш не имеет механизма измерения размера записей. Если задано ограничение размера кэша, все записи должны указать размер. Среда выполнения ASP.NET Core не ограничивает размер кэша на основе давления памяти. Ответственность за ограничение размера кэша лежит на разработчике. Размер указан в тех единицах, которые выбрал разработчик.
Рассмотрим пример.
- Если веб-приложение в основном занималось кэшированием строк, размер каждой записи кэша может быть равен длине строки.
- Приложение может указать размер всех записей как 1, а ограничение размера — количество записей.
Если SizeLimit не задан, кэш растет без ограничения. Среда выполнения ASP.NET Core не обрезает кэш, если системная память низка. Приложения должны быть спроектированы так, чтобы:
Следующий код создает необъединенный фиксированный размер MemoryCache , доступный путем внедрения зависимостей:
public class MyMemoryCache
{
public MemoryCache Cache { get; } = new MemoryCache(
new MemoryCacheOptions
{
SizeLimit = 1024
});
}
SizeLimit
не имеет единиц. Кэшированные записи должны указывать размер в любых единицах, которые они считают наиболее подходящими, если задано ограничение размера кэша. Все пользователи экземпляра кэша должны использовать одну и ту же систему единиц. Запись не будет кэширована, если сумма кэшированных размеров записи превышает значение, указанное в SizeLimit
параметре . Если ограничение размера кэша не задано, размер кэша, заданный в записи, игнорируется.
Следующий код регистрирует MyMemoryCache
в контейнере внедрения зависимостей :
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSingleton<MyMemoryCache>();
MyMemoryCache
создается в качестве независимого кэша памяти для компонентов, которые знают об этом ограниченном кэше и знают, как задать размер записи кэша соответствующим образом.
Размер записи кэша можно задать с помощью SetSize метода расширения или Size свойства:
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);
}
В приведенном выше коде две выделенные строки обеспечивают одинаковый результат задания размера записи кэша.
SetSize
предоставляется для удобства при подключении вызовов к new MemoryCacheOptions()
цепочке.
MemoryCache.Compact
MemoryCache.Compact
пытается удалить указанный процент кэша в следующем порядке:
- Все элементы с истекшим сроком действия.
- Элементы по приоритету. Сначала удаляются элементы с наименьшим приоритетом.
- Наименее недавно использованные объекты.
- Элементы с самым ранним абсолютным сроком действия.
- Элементы с самым ранним скользящим сроком действия.
Закрепленные элементы с приоритетом NeverRemoveникогда не удаляются. Следующий код удаляет элемент кэша и вызывает Compact
, чтобы удалить 25% кэшированных записей.
_myMemoryCache.Cache.Remove(CacheKeys.Entry);
_myMemoryCache.Cache.Compact(.25);
Дополнительные сведения см. в источнике Compact на сайте GitHub.
Зависимости кэша
В следующем примере показано, как удалить запись кэша, если истекает срок действия зависимой записи. Элемент CancellationChangeToken добавляется в кэш. Когда Cancel
вызывается на CancellationTokenSource
, обе записи кэша удаляются.
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 позволяет вытеснить несколько записей кэша в качестве группы. При использовании шаблона using
в коде выше, записи кэша, созданные внутри области using
, наследуют триггеры и параметры срока действия.
Дополнительные примечания
Истечение срока не выполняется в фоновом режиме. Нет таймера, который активно сканирует кэш для истекших элементов. Любое действие в кэше (
Get
,TryGetValue
,Set
, )Remove
может активировать фоновое сканирование просроченных элементов. Таймер наCancellationTokenSource
(CancelAfter) также удаляет запись и запускает проверку просроченных элементов. В следующем примере используется CancellationTokenSource(TimeSpan) для зарегистрированного токена. Когда этот маркер срабатывает, он немедленно удаляет запись и запускает обратные вызовы вытеснения: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); }
При использовании обратного вызова для повторного заполнения элемента кэша:
- Несколько запросов могут найти кэшированное значение ключа пустым, так как обратный вызов не завершен.
- Это может привести к тому, что несколько потоков повторно заполнят кэшированный элемент.
Если одна запись кэша используется для создания другой, дочерний элемент копирует маркеры окончания срока действия родительской записи и параметры срока действия по времени. Дочерний элемент не удаляется вручную при удалении или обновлении родительской записи.
Используйте PostEvictionCallbacks для задания обратных вызовов, которые будут выполнены после удаления записи из кэша.
Для большинства приложений
IMemoryCache
включено. Например, вызовAddMvc
,AddControllersWithViews
,AddRazorPages
,AddMvcCore().AddRazorViewEngine
и многих других методовAdd{Service}
вProgram.cs
включаетIMemoryCache
. Для приложений, которые не вызывают один из предыдущихAdd{Service}
методов, может потребоваться вызвать AddMemoryCache вProgram.cs
.
Обновление фонового кэша
Используйте фоновую службу , например IHostedService для обновления кэша. Фоновая служба может пересчитать записи, а затем поместить их в кэш только когда они будут готовы.
Дополнительные ресурсы
- Просмотреть или скачать образец кода (описание загрузки)
- Распределенное кэширование в ASP.NET Core
- Обнаружение изменений с помощью маркеров изменений в ASP.NET Core
- Кэширование ответов в ASP.NET Core
- Промежуточное ПО для кэширования ответов в ASP.NET Core
- Вспомогательный компонент тега кэша в ASP.NET Core MVC
- Вспомогательный компонент тега распределенного кэша в ASP.NET Core
Просмотреть или скачать образец кода (описание загрузки)
Основы кэширования
Кэширование может значительно повысить производительность и масштабируемость приложения, сокращая работу, необходимую для создания содержимого. Кэширование лучше всего работает с данными, которые изменяются нечасто и требуют значительных ресурсов для генерации. Кэширование создает копию данных, которые могут возвращаться гораздо быстрее, чем из источника. Приложения должны разрабатываться и тестироваться, чтобы никогда не зависеть от кэшированных данных.
ASP.NET Core поддерживает несколько разных кэшей. Самый простой кэш основан на IMemoryCache.
IMemoryCache
представляет кэш, хранящийся в памяти веб-сервера. Приложения, работающие на ферме серверов (несколько серверов), должны гарантировать, что сеансы закреплены при использовании кэша в памяти. Привязанные сессии гарантируют, что последующие запросы от клиента отправляются на один и тот же сервер. Например, веб-приложения Azure используют маршрутизацию запросов приложений (ARR) для маршрутизации всех последующих запросов на один и тот же сервер.
Нелипкие сеансы в веб-ферме требуют распределенного кэша, чтобы избежать проблем согласованности кэша. Для некоторых приложений распределенный кэш может поддерживать более высокий масштаб, чем кэш в памяти. Использование распределенного кэша отгрузит память кэша во внешний процесс.
Кэш в памяти может хранить любой объект. Интерфейс распределенного кэша ограничен byte[]
. Элементы кэша в памяти и распределенном кэше хранятся в виде пар "ключ-значение".
System.Runtime.Caching/MemoryCache
System.Runtime.Caching / MemoryCache (пакет NuGet) можно использовать с:
- .NET Standard 2.0 или более поздней версии.
- Любая реализация .NET , предназначенная для .NET Standard 2.0 или более поздней версии. Например, ASP.NET Core 3.1 или более поздней версии.
- .NET Framework 4.5 или более поздней версии.
Microsoft.Extensions.Caching.Memory/IMemoryCache
(описанная в этой статье) рекомендуетсяSystem.Runtime.Caching
/ использовать вместоMemoryCache
, так как она лучше интегрирована в ASP.NET Core. Например, IMemoryCache
работает в собственном коде с внедрением ASP.NET Основных зависимостей.
Используйте System.Runtime.Caching
/MemoryCache
в качестве моста совместимости при переносе кода из ASP.NET 4.x в ASP.NET Core.
Рекомендации по кешированию
- Код всегда должен иметь резервный вариант для получения данных и не зависит от кэшированного значения.
- Кэш использует дефицитный ресурс, память. Ограничение роста кэша:
- Не используйте внешние входные данные в качестве ключей кэша.
- Используйте срок действия, чтобы ограничить рост кэша.
- Используйте SetSize, Size и SizeLimit, чтобы ограничить размер кэша. Среда выполнения ASP.NET Core не ограничивает размер кэша на основе давления памяти. Разработчик должен ограничить размер кэша.
Используйте IMemoryCache
Предупреждение
Использование общего кэша памяти из внедрения зависимостей и вызов SetSize
, Size
или SizeLimit
для ограничения размера кэша, может привести к сбою приложения. Если для кэша задано ограничение размера, все записи должны указать размер при добавлении. Это может привести к проблемам, так как разработчики могут не иметь полного контроля над тем, что использует общий кэш.
При использовании SetSize
, (Size
или SizeLimit
) чтобы ограничить использование кэша, создайте одиночный объект кэша для кэширования. Дополнительные сведения и пример см. в разделе Используйте SetSize, Size и SizeLimit, чтобы ограничить размер кэша.
Кэш, который используется совместно с другими фреймворками или библиотеками.
Кэширование в памяти — это служба , на которую ссылается приложение с помощью внедрения зависимостей. Запросите экземпляр IMemoryCache
в конструкторе:
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
Следующий код использует TryGetValue, чтобы проверить, находится ли время в кэше. Если время не кэшируется, создается новая запись и добавляется в кэш.Set Класс CacheKeys
является частью образца загрузки.
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);
}
Отображается текущее время и кэшированное время:
@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>
Следующий код использует метод расширения Set
для кэширования данных на относительное время без создания объекта 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);
}
Кэшированное DateTime
значение остается в кэше во время выполнения запросов в течение периода ожидания.
Следующий код использует GetOrCreate и GetOrCreateAsync кэширует данные.
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);
}
Следующий код вызывает Get, чтобы получить кэшированное время.
public IActionResult CacheGet()
{
var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
return View("Cache", cacheEntry);
}
Следующий код получает или создает кэшированный элемент с абсолютным истечением срока действия:
public IActionResult CacheGetOrCreateAbs()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
return DateTime.Now;
});
return View("Cache", cacheEntry);
}
Кэшированный набор элементов, у которого только скользящее время истечения, может никогда не истекать. Если к кэшированному элементу неоднократно обращаются в пределах скользящего интервала истечения, срок действия элемента никогда не истекает. Объедините скользящий срок действия с абсолютным сроком действия, чтобы гарантировать истечение срока действия элемента. Абсолютное время истечения устанавливает верхнюю границу времени, в течение которого элемент может быть кэширован, при этом элемент может истечь раньше, если он не запрашивается в течение скользящего интервала. Если истекает либо скользящий интервал истечения срока действия, либо абсолютное время окончания срока действия, элемент удаляется из кэша.
Следующий код получает или создает кэшированный элемент с скользящим и абсолютным сроком действия:
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);
}
Предыдущий код гарантирует, что данные не будут кэшироваться дольше абсолютного времени.
GetOrCreate, GetOrCreateAsync, и Get являются методами расширения в классе CacheExtensions. Эти методы расширяют возможности IMemoryCache.
MemoryCacheEntryOptions
Следующий пример:
- Задает скользящее время истечения срока действия. Запросы, обращающиеся к этому кэшированному элементу, обнуляют таймер скользящего истечения.
- Задает приоритет CacheItemPriority.NeverRemoveкэша.
- Задает объект PostEvictionDelegate , который будет вызываться после того, как запись вытеснится из кэша. Обратный вызов выполняется в другом потоке кода, который удаляет элемент из кэша.
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);
}
Использование SetSize, Size и SizeLimit для ограничения размера кэша
Экземпляр MemoryCache
может опционально указать и применить ограничение размера. Ограничение размера кэша не имеет определенной единицы измерения, так как кэш не имеет механизма измерения размера записей. Если задано ограничение размера кэша, все записи должны указать размер. Среда выполнения ASP.NET Core не ограничивает размер кэша на основе давления памяти. На усмотрение разработчика ограничить размер кэша. Размер указан в единицах измерения, выбранных разработчиком.
Рассмотрим пример.
- Если веб-приложение в основном кэшировало строки, размер каждой записи кэша мог бы быть равен длине строки.
- Приложение может указать размер всех записей как 1, а ограничение размера — количество записей.
Если SizeLimit не установлено, кэш растет без ограничения. Среда выполнения ASP.NET Core не обрезает кэш, если системная память низка. Приложения должны быть спроектированы следующим образом:
Следующий код создает необъединенный фиксированный размер MemoryCache , доступный путем внедрения зависимостей:
// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache
{
public MemoryCache Cache { get; private set; }
public MyMemoryCache()
{
Cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 1024
});
}
}
SizeLimit
не имеет единиц. Кэшированные записи должны указывать размер в любых единицах, которые они считают наиболее подходящими, если задано ограничение размера кэша. Все пользователи экземпляра кэша должны использовать одну и ту же систему единиц. Запись не будет кэширована, если сумма кэшированных размеров записи превышает указанное значение SizeLimit
. Если ограничение размера кэша не задано, размер кэша, заданный в записи, будет игнорироваться.
Код ниже регистрирует MyMemoryCache
в контейнере внедрения зависимостей.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<MyMemoryCache>();
}
MyMemoryCache
создается в качестве независимого кэша памяти для компонентов, которые знают об этом ограниченном кэше и знают, как задать размер записи кэша соответствующим образом.
В приведенном ниже коде используется 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");
}
}
Размер записи в кэше можно задать с помощью Size или методов расширения SetSize.
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
пытается удалить указанный процент кэша в следующем порядке:
- Все элементы с истекшим сроком действия.
- Элементы по приоритету. Сначала удаляются элементы с наименьшим приоритетом.
- Наименее недавно использованные объекты.
- Элементы с самым ранним абсолютным сроком действия.
- Элементы с самым ранним скользящим сроком действия.
Закрепленные элементы с приоритетом NeverRemove никогда не удаляются. Следующий код удаляет элемент кэша и вызывает Compact
:
_cache.Remove(MyKey);
// Remove 33% of cached items.
_cache.Compact(.33);
cache_size = _cache.Count;
Дополнительные сведения см. в источнике Compact на сайте GitHub.
Зависимости кэша
В следующем примере показано, как удалить элемент кэша, если истекает срок действия зависимого элемента. Объект CancellationChangeToken добавляется в кэшированный элемент. При вызове Cancel
на CancellationTokenSource
обе записи кэша удаляются.
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
При использовании шаблона в приведенном выше коде записи кэша, созданные внутри using
блока, наследуют триггеры и параметры срока действия.
Дополнительные примечания
Истечение срока действия не происходит автоматически. Таймера, который активно сканирует кэш для просроченных элементов, не существует. Любое действие в кэше (
Get
,Set
, )Remove
может активировать фоновое сканирование просроченных элементов. Таймер наCancellationTokenSource
(CancelAfter) также удаляет запись и запускает проверку просроченных элементов. В следующем примере используется CancellationTokenSource(TimeSpan) для зарегистрированного токена. Когда этот маркер срабатывает, он немедленно удаляет запись и вызывает обратные вызовы вытеснения: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); }
При использовании обратного вызова для повторного заполнения элемента кэша:
- Несколько запросов могут найти кэшированное значение ключа пустым, так как обратный вызов не завершен.
- Это может привести к тому, что несколько потоков повторно заполняют кэшированный элемент.
Если одна запись кэша используется для создания другой, дочерний элемент копирует маркеры окончания срока действия родительской записи и настройки срока действия, основанные на времени. Дочерний элемент не удаляется вручную удалением или обновлением родительской записи.
С помощью PostEvictionCallbacks задаются обратные вызовы, которые будут выполнены после удаления записи из кэша. В примере кода CancellationTokenSource.Dispose() вызывается для освобождения неуправляемых ресурсов, используемых
CancellationTokenSource
. ОднакоCancellationTokenSource
не удаляется немедленно, так как он по-прежнему используется записью кэша.CancellationToken
передается кMemoryCacheEntryOptions
для создания записи в кэше, которая истекает через определенное время. ПоэтомуDispose
не следует вызывать до тех пор, пока запись кэша не будет удалена или истекла. В примере кода метод RegisterPostEvictionCallback вызывается для регистрации обратного вызова, который будет вызываться при вытеснении записи кэша, и в этом обратном вызове будет выполняться удалениеCancellationTokenSource
.Для большинства приложений
IMemoryCache
включен. Например, вызовAddMvc
,AddControllersWithViews
,AddRazorPages
,AddMvcCore().AddRazorViewEngine
и многие другиеAdd{Service}
методы вConfigureServices
, включаетIMemoryCache
. Для приложений, которые не вызывают один из предыдущихAdd{Service}
методов, может потребоваться вызвать AddMemoryCache вConfigureServices
.
Обновление фонового кэша
Используйте фоновую службу , например IHostedService для обновления кэша. Фоновая служба может пересчитывать записи, а затем помещать их в кэш только после их готовности.
Дополнительные ресурсы
- Распределенное кэширование в ASP.NET Core
- Обнаружение изменений с помощью маркеров изменений в ASP.NET Core
- Кэширование ответов в ASP.NET Core
- Промежуточное ПО для кэширования ответов в ASP.NET Core
- Вспомогательный компонент тега кэша в ASP.NET Core MVC
- Вспомогательный компонент тега распределенного кэша в ASP.NET Core
ASP.NET Core