Обучение
Модуль
Повышение производительности с помощью кэша в проекте .NET Aspire - Training
В этом модуле вы узнаете о кэшах в облачном приложении .NET Aspire и о том, как использовать их для оптимизации производительности микрослужб.
Этот браузер больше не поддерживается.
Выполните обновление до Microsoft Edge, чтобы воспользоваться новейшими функциями, обновлениями для системы безопасности и технической поддержкой.
В этой статье объясняется, как настроить и использовать библиотеку HybridCache
в приложении ASP.NET Core. Введение в библиотеку см. в разделе Обзор кэшированияHybridCache
.
Установите пакет Microsoft.Extensions.Caching.Hybrid
.
dotnet add package Microsoft.Extensions.Caching.Hybrid
Добавьте 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
состояния рекомендуется для большинства сценариев. Код для вызова является относительно простым. Приведем пример:
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
123customerId
421 с orderId
23(оба из которых создают ключ кэша order42123
)
Это руководство применяется одинаково к любому API кэша на основе string
, например HybridCache
, IDistributedCache
и IMemoryCache
.
Обратите внимание, что встроенный синтаксис интерполированной строки ($"..."
в предыдущих примерах допустимых ключей) находится непосредственно внутри вызова GetOrCreateAsync
. Этот синтаксис рекомендуется использовать при использовании HybridCache
, так как он позволяет выполнять запланированные будущие улучшения, которые обходят необходимость выделения string
для ключа во многих сценариях.
HybridCache
по умолчанию (через AddHybridCache(...)
) ограничивает ключи 1024 символами по умолчанию. Настройка этого числа осуществляется через HybridCacheOptions.MaximumKeyLength
, при этом более длинные ключи обходят механизмы кеша, чтобы предотвратить насыщение.IDistributedCache
, конкретная реализация серверной части может налагать дополнительные ограничения. В качестве гипотетического примера определенный бэкенд может использовать логику ключей без учета регистра. По умолчанию HybridCache
(через AddHybridCache(...)
) обнаруживает этот сценарий, чтобы предотвратить атаки путаницы, однако это может по-прежнему привести к тому, что конфликтующие ключи могут быть перезаписаны или вытеснены раньше, чем ожидалось.Альтернативная перегрузка может снизить некоторые издержки от захваченных переменных и обратных вызовов на экземпляр, но за счет более сложного кода. В большинстве случаев увеличение производительности не перевешивает сложность кода. Ниже приведен пример использования альтернативной перегрузки:
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;
}
}
Во многих сценариях 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
позволяют настроить ограничения, которые применяются ко всем записям кэша:
Использование вторичного внепроцессного кэша требует сериализации. Сериализация настраивается как часть регистрации 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)]
указывает, что состояние объекта невозможно изменить после его создания.
HybridCache
также предоставляет дополнительные API для IDistributedCache
внедрений, чтобы избежать выделения byte[]
. Эта функция реализована в предварительных версиях пакетов Microsoft.Extensions.Caching.StackExchangeRedis
и Microsoft.Extensions.Caching.SqlServer
. Дополнительные сведения см. в разделе IBufferDistributedCache.
Ниже приведены команды .NET CLI для установки пакетов:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
Конкретная реализация абстрактного HybridCache
класса включается в общую платформу и предоставляется с помощью внедрения зависимостей. Но разработчики могут предоставлять или использовать пользовательские реализации API, например FusionCache.
Библиотека HybridCache
поддерживает ранние версии сред выполнения .NET, начиная с платформы .NET Framework 4.7.2 и .NET Standard 2.0.
Дополнительные сведения о HybridCache
см. в следующих ресурсах:
HybridCache
исходного кодаОтзыв о ASP.NET Core
ASP.NET Core — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Обучение
Модуль
Повышение производительности с помощью кэша в проекте .NET Aspire - Training
В этом модуле вы узнаете о кэшах в облачном приложении .NET Aspire и о том, как использовать их для оптимизации производительности микрослужб.