Библиотека HybridCache в ASP.NET Core
Внимание
HybridCache
в настоящее время по-прежнему находится в предварительной версии, но будет полностью выпущен после .NET 9.0 в будущем дополнительном выпуске расширений .NET.
В этой статье объясняется, как настроить и использовать библиотеку HybridCache
в приложении ASP.NET Core. Общие сведения о библиотеке см . в HybridCache
разделе обзора кэширования.
Получение библиотеки
Установите пакет Microsoft.Extensions.Caching.Hybrid
.
dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"
Регистрация службы
Добавьте службу в HybridCache
контейнер внедрения зависимостей (DI), вызвав:AddHybridCache
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
builder.Services.AddHybridCache();
Предыдущий код регистрирует HybridCache
службу с параметрами по умолчанию. API регистрации также может настраивать параметры и сериализацию.
Получение и хранение записей кэша
Служба HybridCache
предоставляет GetOrCreateAsync
метод с двумя перегрузками, принимая ключ и:
- Метод фабрики.
- Состояние и метод фабрики.
Метод использует ключ для получения объекта из первичного кэша. Если элемент не найден в основном кэше (отсутствует кэш), он проверяет дополнительный кэш, если он настроен. Если данные отсутствуют (еще один кэш отсутствует), он вызывает метод фабрики, чтобы получить объект из источника данных. Затем он сохраняет объект как в первичных, так и вторичных кэшах. Метод фабрики никогда не вызывается, если объект найден в первичном или вторичном кэше (попадание кэша).
Служба HybridCache
гарантирует, что только один одновременный вызывающий объект для заданного ключа вызывает метод фабрики, а все остальные вызывающие ожидают результата этого вызова. Переданная CancellationToken
функция GetOrCreateAsync
представляет объединенную отмену всех одновременных вызывающих объектов.
Основная GetOrCreateAsync
перегрузка
Перегрузка без отслеживания GetOrCreateAsync
состояния рекомендуется для большинства сценариев. Код для вызова является относительно простым. Приведем пример:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Руководство по созданию ключей кэша
key
, передаваемый в GetOrCreateAsync
, должен однозначно идентифицировать данные, которые кэшируются:
- С точки зрения значений идентификатора, используемых для извлечения этих данных из источника.
- С точки зрения других данных, кэшированных в приложении.
Оба типа уникальности обычно обеспечиваются с помощью объединения строк для создания одной строки ключа, состоящей из разных частей, объединенных в одну строку. Например:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
или
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
Это ответственность вызывающего абонента, чтобы убедиться, что ключевая схема действительна и не может привести к путанице данных.
Рекомендуется не использовать входные данные внешнего пользователя в ключе кэша. Например, не используйте необработанные значения string
из пользовательского интерфейса в рамках ключа кэша. Такие ключи могут разрешать попытки вредоносного доступа или использовать в атаке типа "отказ в обслуживании", насытив кэш с данными, имеющими бессмысленные ключи, созданные из случайных строк. В предыдущих допустимых примерах данные заказа и предпочтения пользователя явно различаются:
-
orderid
иuserId
являются внутренними идентификаторами. -
region
может быть перечислением или строкой из предопределенного списка известных регионов.
Значимость не придаётся таким маркерам, как /
или _
. Все значение ключа рассматривается как непрозрачная строка идентификации. В этом случае можно опустить /
и _
без изменений в способе функций кэша, но разделитель обычно используется для предотвращения неоднозначности, например $"order{customerId}{orderId}"
может вызвать путаницу между:
-
customerId
42 сorderId
123 -
customerId
421 сorderId
23
(оба из которых создают ключ кэша order42123
)
Это руководство применяется одинаково к любому API кэша на основе string
, например HybridCache
, IDistributedCache
и IMemoryCache
.
Обратите внимание, что встроенный синтаксис интерполированной строки ($"..."
в предыдущих примерах допустимых ключей) находится непосредственно внутри вызова GetOrCreateAsync
. Этот синтаксис рекомендуется использовать при использовании HybridCache
, так как он позволяет выполнять запланированные будущие улучшения, которые обходят необходимость выделения string
для ключа во многих сценариях.
Дополнительные ключевые аспекты
- Ключи могут быть ограничены допустимой максимальной длиной. Например, реализация
HybridCache
по умолчанию (черезAddHybridCache(...)
) ограничивает ключи 1024 символами по умолчанию. Настройка этого числа осуществляется черезHybridCacheOptions.MaximumKeyLength
, при этом более длинные ключи обходят механизмы кеша, чтобы предотвратить насыщение. - Ключи должны быть допустимыми последовательностями Юникода. Если передаются недопустимые последовательности Юникода, поведение не определено.
- При использовании внепроцессного вторичного кэша, например
IDistributedCache
, конкретная реализация серверной части может налагать дополнительные ограничения. В качестве гипотетического примера определенный бэкенд может использовать логику ключей без учета регистра. По умолчаниюHybridCache
(черезAddHybridCache(...)
) обнаруживает этот сценарий, чтобы предотвратить атаки путаницы, однако это может по-прежнему привести к тому, что конфликтующие ключи могут быть перезаписаны или вытеснены раньше, чем ожидалось.
Альтернативная GetOrCreateAsync
перегрузка
Альтернативная перегрузка может снизить некоторые издержки от захваченных переменных и обратных вызовов на экземпляр, но за счет более сложного кода. В большинстве случаев увеличение производительности не перевешивает сложность кода. Ниже приведен пример использования альтернативной перегрузки:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
(name, id, obj: this),
static async (state, token) =>
await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Метод SetAsync
Во многих сценариях GetOrCreateAsync
это единственный необходимый API. Но HybridCache
также необходимо SetAsync
сохранить объект в кэше, не пытаясь сначала получить его.
Удаление записей кэша по ключу
Когда базовые данные для записи кэша изменяются до истечения срока действия, удалите запись явным образом, вызвав RemoveAsync
ключ для записи.
Перегрузка позволяет указать коллекцию значений ключей.
При удалении записи удаляется как из основного, так и из вторичных кэшей.
Удаление записей кэша по тегу
Внимание
Эта функция по-прежнему находится в процессе разработки. Если вы пытаетесь удалить записи по тегу, вы заметите, что он не имеет никакого эффекта.
Теги можно использовать для группирования записей кэша и их объединения.
Задайте теги при вызове GetOrCreateAsync
, как показано в следующем примере:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Удалите все записи для указанного тега, вызвав RemoveByTagAsync
значение тега.
Перегрузка позволяет указать коллекцию значений тегов.
При удалении записи удаляется как из основного, так и из вторичных кэшей.
Параметры
Этот AddHybridCache
метод можно использовать для настройки глобальных значений по умолчанию. В следующем примере показано, как настроить некоторые доступные параметры:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024;
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
Метод GetOrCreateAsync
также может взять HybridCacheEntryOptions
объект для переопределения глобальных значений по умолчанию для определенной записи кэша. Приведем пример:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Дополнительные сведения о параметрах см. в исходном коде:
Ограничения
Следующие свойства HybridCacheOptions
позволяют настроить ограничения, которые применяются ко всем записям кэша:
- MaximumPayloadBytes — максимальный размер записи кэша. Значение по умолчанию — 1 МБ. Попытки сохранить значения по этому размеру регистрируются, и значение не хранится в кэше.
- MaximumKeyLength — максимальная длина ключа кэша. Значение по умолчанию — 1024 символов. Попытки сохранить значения по этому размеру регистрируются, и значение не хранится в кэше.
Сериализация
Использование вторичного внепроцессного кэша требует сериализации. Сериализация настраивается как часть регистрации HybridCache
службы. Сериализаторы типа и сериализаторы общего назначения можно настроить с помощью AddSerializer
методов и AddSerializerFactory
методов, связанных с вызовом AddHybridCache
. По умолчанию библиотека обрабатывает string
и byte[]
внутренне и использует System.Text.Json
все остальное.
HybridCache
также можно использовать другие сериализаторы, такие как protobuf или XML.
В следующем примере служба настраивает службу для использования сериализатора protobuf с определенным типом:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializer<SomeProtobufMessage,
GoogleProtobufSerializer<SomeProtobufMessage>>();
В следующем примере служба настраивает использование сериализатора protobuf общего назначения, который может обрабатывать множество типов protobuf:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();
Для дополнительного кэша требуется хранилище данных, например Redis или SqlServer. Например, чтобы использовать Кэш Azure для Redis, выполните приведенные ниже действия.
Установите пакет
Microsoft.Extensions.Caching.StackExchangeRedis
.Создайте экземпляр Кэш Azure для Redis.
Получите строка подключения, которая подключается к экземпляру Redis. Найдите строка подключения, выбрав "Показать ключи доступа" на странице "Обзор" в портал Azure.
Сохраните строка подключения в конфигурации приложения. Например, используйте файл секретов пользователя, который выглядит как следующий JSON, с строка подключения в
ConnectionStrings
разделе. Замените<the connection string>
фактическим строка подключения:{ "ConnectionStrings": { "RedisConnectionString": "<the connection string>" } }
Зарегистрируйтесь в di
IDistributedCache
реализации, которую предоставляет пакет Redis. Для этого вызовите и передайтеAddStackExchangeRedisCache
строка подключения. Например:builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString"); });
Реализация Redis
IDistributedCache
теперь доступна из контейнера DI приложения.HybridCache
использует его в качестве дополнительного кэша и использует сериализатор, настроенный для него.
Дополнительные сведения см. в примере приложения для сериализации HybridCache.
Хранилище кэша
По умолчанию HybridCache
используется MemoryCache для основного хранилища кэша. Записи кэша хранятся в процессе, поэтому каждый сервер имеет отдельный кэш, который теряется при перезапуске процесса сервера. Для дополнительного внепроцессного хранилища, например Redis или SQL Server, HybridCache
использует настроенную IDistributedCache
реализацию, если она есть. Но даже без реализации IDistributedCache
, служба HybridCache
по-прежнему обеспечивает внутреннепроцессное кэширование и защиту от лавины .
Заметка
При недопустимости записей кэша по ключу или по тегам они недопустимы на текущем сервере и в дополнительном внепроцессном хранилище. Однако кэш в памяти на других серверах не затрагивается.
Оптимизация производительности
Чтобы оптимизировать производительность, настройте HybridCache
повторное использование объектов и избегайте byte[]
выделения.
Повторное использование объектов
Повторно используя экземпляры, можно сократить затраты на выделение ЦП и объектов, HybridCache
связанных с десериализацией каждого вызова. Это может привести к улучшению производительности в сценариях, когда кэшированные объекты являются большими или часто доступны.
В типичном существующем коде, который использует IDistributedCache
, каждый извлечение объекта из кэша приводит к десериализации. Это означает, что каждый одновременный вызывающий объект получает отдельный экземпляр объекта, который не может взаимодействовать с другими экземплярами. Результатом является безопасность потоков, так как не существует риска параллельных изменений в одном экземпляре объекта.
Так как много HybridCache
использования будет адаптировано из существующего IDistributedCache
кода, сохраняет это поведение по умолчанию, HybridCache
чтобы избежать возникновения ошибок параллелизма. Однако объекты по сути являются потокобезопасными, если:
- Они являются неизменяемыми типами.
- Код не изменяет их.
В таких случаях сообщите HybridCache
, что можно повторно использовать экземпляры следующим образом:
- Пометка типа как
sealed
. Ключевоеsealed
слово в C# означает, что класс не может быть унаследован. - Применение атрибута
[ImmutableObject(true)]
к типу. Атрибут[ImmutableObject(true)]
указывает, что состояние объекта невозможно изменить после его создания.
Избегайте byte[]
выделения
HybridCache
также предоставляет необязательные API для IDistributedCache
реализаций, чтобы избежать byte[]
выделения. Эта функция реализуется предварительными версиями и Microsoft.Extensions.Caching.StackExchangeRedis
пакетамиMicrosoft.Extensions.Caching.SqlServer
. Дополнительные сведения см. в разделе IBufferDistributedCache Ниже приведены команды интерфейса командной строки .NET для установки пакетов:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
Пользовательские реализации HybridCache
Конкретная реализация абстрактного HybridCache
класса включается в общую платформу и предоставляется с помощью внедрения зависимостей. Но разработчики могут предоставлять пользовательские реализации API.
Совместимость
Библиотека HybridCache
поддерживает старые среды выполнения .NET до платформа .NET Framework 4.7.2 и .NET Standard 2.0.
Дополнительные ресурсы
Дополнительные сведения см HybridCache
. в следующих ресурсах:
- Проблема GitHub dotnet/aspnetcore #54647.
-
Исходный код
HybridCache
ASP.NET Core