Распределенное кэширование в ASP.NET Core

Мохсин Насир и смандия

Распределенный кэш — это кэш, совместно используемый несколькими серверами приложений, обычно обслуживаемый как внешняя служба для серверов приложений, обращаюющихся к нему. Распределенный кэш может повысить производительность и масштабируемость приложения ASP.NET Core, особенно если приложение размещается в облачной службе или ферме серверов.

Распределенный кэш имеет несколько преимуществ по сравнению с другими сценариями кэширования, в которых кэшированные данные хранятся на отдельных серверах приложений.

При распределении кэшированных данных данные:

  • Согласована (согласована) между запросами на несколько серверов.
  • Выживает перезапуски сервера и развертывания приложений.
  • Не использует локальную память.

Конфигурация распределенного кэша зависит от реализации. В этой статье описывается настройка распределенных кэшей SQL Server и Redis. Кроме того, доступны сторонние реализации, такие как NCache (NCache на GitHub). Независимо от выбранной реализации приложение взаимодействует с кэшем IDistributedCache с помощью интерфейса.

Просмотреть или скачать образец кода (как скачивать)

Предварительные требования

Чтобы использовать распределенный кэш SQL Server, добавьте ссылку на пакет Microsoft.Extensions.Caching.SqlServer .

Чтобы использовать распределенный кэш Redis, добавьте ссылку на пакет Microsoft.Extensions.Caching.StackExchangeRedis .

Чтобы использовать распределенный кэш NCache, добавьте ссылку на пакет NCache.Microsoft.Extensions.Caching.OpenSource .

Интерфейс IDistributedCache

Интерфейс IDistributedCache предоставляет следующие методы для управления элементами в реализации распределенного кэша:

  • Get, GetAsync: принимает строковый ключ и извлекает кэшированный элемент в виде byte[] массива, если он найден в кэше.
  • Set, SetAsync: добавляет элемент (в виде byte[] массива) в кэш с помощью строкового ключа.
  • Refresh, RefreshAsync: обновляет элемент в кэше на основе ключа, сбрасывая время ожидания скользящего срока действия (если таковой имеется).
  • Remove, RemoveAsync: удаляет элемент кэша на основе его строкового ключа.

Создание распределенных служб кэширования

Регистрация реализации IDistributedCache в Program.cs. Реализации, предоставляемые платформой, описанные в этом разделе, включают:

Кэш распределенной памяти

Кэш распределенной памяти (AddDistributedMemoryCache) — это предоставляемая платформой реализация IDistributedCache , в которой хранятся элементы в памяти. Кэш распределенной памяти не является фактическим распределенным кэшем. Кэшированные элементы хранятся экземпляром приложения на сервере, где выполняется приложение.

Кэш распределенной памяти является полезной реализацией:

  • В сценариях разработки и тестирования.
  • Если один сервер используется в рабочей среде, а потребление памяти не является проблемой. Реализация кэша распределенной памяти абстрагирует кэшированное хранилище данных. Это позволяет реализовать истинно распределенное решение кэширования в будущем, если потребуется несколько узлов или отказоустойчивости.

Пример приложения использует кэш распределенной памяти при запуске приложения в среде разработки в Program.cs:

builder.Services.AddDistributedMemoryCache();

Распределенный кэш SQL Server

Реализация распределенного кэша SQL Server (AddDistributedSqlServerCache) позволяет распределенной кэшу использовать базу данных SQL Server в качестве резервного хранилища. Чтобы создать кэшированную таблицу элементов SQL Server в экземпляре SQL Server, можно использовать sql-cache это средство. Средство создает таблицу с указанным именем и схемой.

Создайте таблицу в SQL Server, выполнив sql-cache create команду. Укажите экземпляр SQL Server (Data Source), базу данных (Initial Catalog), схему (например, dbo) и имя таблицы (например, TestCache):

dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

В журнал записывается сообщение, указывающее, что средство выполнено успешно:

Table and index were created successfully.

Таблица, sql-cache созданная средством, имеет следующую схему:

SqlServer Cache Table

Примечание

Приложение должно управлять значениями кэша с помощью экземпляраIDistributedCache, а не .SqlServerCache

Пример приложения реализуется в среде, отличной от разработки, в Program.csследующих средахSqlServerCache:

builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString(
        "DistCache_ConnectionString");
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

Примечание

A ConnectionString (и при необходимости SchemaName ) TableNameобычно хранятся за пределами системы управления версиями (например, хранятся диспетчером секретов или в appsettings.json/appsettings.{Environment}.json файлах). Строка подключения может содержать учетные данные, которые должны храниться вне систем управления версиями.

Распределенный кэш Redis

Redis -это хранилище данных в памяти с открытым исходным кодом, которое часто используется в качестве распределенного кэша. Вы можете настроить кэш Redis для Azure для размещенного в Azure приложения ASP.NET Core и использовать кэш Redis для Azure для локальной разработки.

Приложение настраивает реализацию кэша с помощью экземпляра RedisCache (AddStackExchangeRedisCache).

  1. Создайте кэш Azure для Redis.
  2. Скопируйте основную строку подключения (StackExchange.Redis) в конфигурацию.
    • Локальная разработка: сохраните строку подключения с помощью диспетчера секретов.
    • Azure: сохраните строку подключения в конфигурации службы приложений или другом безопасном хранилище.

Следующий код включает кэш Azure для Redis:

builder.Services.AddStackExchangeRedisCache(options =>
 {
     options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");
     options.InstanceName = "SampleInstance";
 });

В приведенном выше коде предполагается, что основная строка подключения (StackExchange.Redis) была сохранена в конфигурации с именем MyRedisConStrключа.

См. сведения на странице с ценами на Azure Cache for Redis.

См. эту проблему GitHub для обсуждения альтернативных подходов к локальному кэшу Redis.

Распределенный кэш NCache

NCache — это распределенный кэш с открытым кодом в памяти, разработанный изначально в .NET и .NET Core. NCache работает как локально, так и настроен как кластер распределенного кэша для приложения ASP.NET Core, работающего в Azure или на других платформах размещения.

Сведения об установке и настройке NCache на локальном компьютере см. в руководстве по началу работы с Windows (.NET и .NET Core).

Чтобы настроить NCache, выполните приведенные действия.

  1. Установите NuGet с открытым кодом NCache.
  2. Настройте кластер кэша в client.ncconf.
  3. Добавьте следующий код в файл Program.cs:
builder.Services.AddNCacheDistributedCache(configuration =>
{
    configuration.CacheName = "democache";
    configuration.EnableLogs = true;
    configuration.ExceptionsEnabled = true;
});

Использование распределенного кэша

Чтобы использовать IDistributedCache интерфейс, запросите экземпляр IDistributedCache приложения. Экземпляр предоставляется путем внедрения зависимостей (DI).

При запуске IDistributedCache примера приложения внедряется в Program.cs. Текущее время кэшируется с помощью IHostApplicationLifetime (дополнительные сведения см. в разделе Generic Host: IHostApplicationLifetime):

app.Lifetime.ApplicationStarted.Register(() =>
{
    var currentTimeUTC = DateTime.UtcNow.ToString();
    byte[] encodedCurrentTimeUTC = System.Text.Encoding.UTF8.GetBytes(currentTimeUTC);
    var options = new DistributedCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromSeconds(20));
    app.Services.GetService<IDistributedCache>()
                              .Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
});

Пример приложения внедряется IDistributedCache на IndexModel страницу "Индекс".

При каждой загрузке страницы индекса кэш проверяется на время кэширования.OnGetAsync Если срок действия кэшированного времени не истек, отображается время. Если 20 секунд прошло с момента последнего доступа к кэшированному времени (после последней загрузки этой страницы), на странице отображается срок действия кэшированного времени.

Немедленно обновите кэшированное время до текущего времени, нажав кнопку сброса кэшированного времени . Кнопка активирует OnPostResetCachedTime метод обработчика.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string? CachedTimeUTC { get; set; }
    public string? ASP_Environment { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }

        ASP_Environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        if (String.IsNullOrEmpty(ASP_Environment))
        {
            ASP_Environment = "Null, so Production";
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

Нет необходимости использовать одноэлементное или ограниченное время существования для IDistributedCache экземпляров со встроенными реализациями.

Кроме того, вы можете создать IDistributedCache экземпляр, где бы он ни нуждался вместо использования внедрения зависимостей, но создание экземпляра в коде может усложнить тестирование и нарушение принципа явных зависимостей.

Рекомендации

При принятии IDistributedCache решения о том, какая реализация лучше всего подходит для вашего приложения, учитывайте следующее:

  • Существующая инфраструктура
  • Требования к производительности
  • Стоимость
  • Взаимодействие с командой

Решения кэширования обычно используют хранилище в памяти для быстрого извлечения кэшированных данных, но память является ограниченным ресурсом и дорогостоящим расширением. Храните только часто используемые данные в кэше.

Как правило, кэш Redis обеспечивает более высокую пропускную способность и меньшую задержку, чем кэш SQL Server. Однако для определения характеристик производительности стратегий кэширования обычно требуется тестирование производительности.

Если SQL Server используется в качестве хранилища резервного копирования распределенного кэша, использование одной и той же базы данных для кэша и обычного хранилища данных приложения и извлечения может отрицательно повлиять на производительность обоих. Рекомендуется использовать выделенный экземпляр SQL Server для хранилища резервного копирования распределенного кэша.

Дополнительные ресурсы

Распределенный кэш — это кэш, совместно используемый несколькими серверами приложений, обычно обслуживаемый как внешняя служба для серверов приложений, обращаюющихся к нему. Распределенный кэш может повысить производительность и масштабируемость приложения ASP.NET Core, особенно если приложение размещается в облачной службе или ферме серверов.

Распределенный кэш имеет несколько преимуществ по сравнению с другими сценариями кэширования, в которых кэшированные данные хранятся на отдельных серверах приложений.

При распределении кэшированных данных данные:

  • Согласована (согласована) между запросами на несколько серверов.
  • Выживает перезапуски сервера и развертывания приложений.
  • Не использует локальную память.

Конфигурация распределенного кэша зависит от реализации. В этой статье описывается настройка распределенных кэшей SQL Server и Redis. Кроме того, доступны сторонние реализации, такие как NCache (NCache на GitHub). Независимо от выбранной реализации приложение взаимодействует с кэшем IDistributedCache с помощью интерфейса.

Просмотреть или скачать образец кода (как скачивать)

Предварительные требования

Чтобы использовать распределенный кэш SQL Server, добавьте ссылку на пакет Microsoft.Extensions.Caching.SqlServer .

Чтобы использовать распределенный кэш Redis, добавьте ссылку на пакет Microsoft.Extensions.Caching.StackExchangeRedis .

Чтобы использовать распределенный кэш NCache, добавьте ссылку на пакет NCache.Microsoft.Extensions.Caching.OpenSource .

Интерфейс IDistributedCache

Интерфейс IDistributedCache предоставляет следующие методы для управления элементами в реализации распределенного кэша:

  • Get, GetAsync: принимает строковый ключ и извлекает кэшированный элемент в виде byte[] массива, если он найден в кэше.
  • Set, SetAsync: добавляет элемент (в виде byte[] массива) в кэш с помощью строкового ключа.
  • Refresh, RefreshAsync: обновляет элемент в кэше на основе ключа, сбрасывая время ожидания скользящего срока действия (если таковой имеется).
  • Remove, RemoveAsync: удаляет элемент кэша на основе его строкового ключа.

Создание распределенных служб кэширования

Регистрация реализации IDistributedCache в Startup.ConfigureServices. Реализации, предоставляемые платформой, описанные в этом разделе, включают:

Кэш распределенной памяти

Кэш распределенной памяти (AddDistributedMemoryCache) — это предоставляемая платформой реализация IDistributedCache , в которой хранятся элементы в памяти. Кэш распределенной памяти не является фактическим распределенным кэшем. Кэшированные элементы хранятся экземпляром приложения на сервере, где выполняется приложение.

Кэш распределенной памяти является полезной реализацией:

  • В сценариях разработки и тестирования.
  • Если один сервер используется в рабочей среде, а потребление памяти не является проблемой. Реализация кэша распределенной памяти абстрагирует кэшированное хранилище данных. Это позволяет реализовать истинно распределенное решение кэширования в будущем, если потребуется несколько узлов или отказоустойчивости.

Пример приложения использует кэш распределенной памяти при запуске приложения в среде разработки в Startup.ConfigureServices:

services.AddDistributedMemoryCache();

Распределенный кэш SQL Server

Реализация распределенного кэша SQL Server (AddDistributedSqlServerCache) позволяет распределенной кэшу использовать базу данных SQL Server в качестве резервного хранилища. Чтобы создать кэшированную таблицу элементов SQL Server в экземпляре SQL Server, можно использовать sql-cache это средство. Средство создает таблицу с указанным именем и схемой.

Создайте таблицу в SQL Server, выполнив sql-cache create команду. Укажите экземпляр SQL Server (Data Source), базу данных (Initial Catalog), схему (например, dbo) и имя таблицы (например, TestCache):

dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

В журнал записывается сообщение, указывающее, что средство выполнено успешно:

Table and index were created successfully.

Таблица, sql-cache созданная средством, имеет следующую схему:

SqlServer Cache Table

Примечание

Приложение должно управлять значениями кэша с помощью экземпляраIDistributedCache, а не .SqlServerCache

Пример приложения реализуется в среде, отличной от разработки, в Startup.ConfigureServicesследующих средахSqlServerCache:

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = 
        _config["DistCache_ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

Примечание

A ConnectionString (и при необходимости SchemaName ) TableNameобычно хранятся за пределами системы управления версиями (например, хранятся диспетчером секретов или в appsettings.json/appsettings.{Environment}.json файлах). Строка подключения может содержать учетные данные, которые должны храниться вне систем управления версиями.

Распределенный кэш Redis

Redis -это хранилище данных в памяти с открытым исходным кодом, которое часто используется в качестве распределенного кэша. Вы можете настроить кэш Redis для Azure для размещенного в Azure приложения ASP.NET Core и использовать кэш Redis для Azure для локальной разработки.

Приложение настраивает реализацию кэша с помощью экземпляра RedisCache (AddStackExchangeRedisCache).

  1. Создайте кэш Azure для Redis.
  2. Скопируйте основную строку подключения (StackExchange.Redis) в конфигурацию.
    • Локальная разработка: сохраните строку подключения с помощью диспетчера секретов.
    • Azure: сохраните строку подключения в конфигурации службы приложений или другом безопасном хранилище.

Следующий код включает кэш Azure для Redis:

public void ConfigureServices(IServiceCollection services)
{
    if (_hostContext.IsDevelopment())
    {
        services.AddDistributedMemoryCache();
    }
    else
    {
        services.AddStackExchangeRedisCache(options =>
        {
            options.Configuration = _config["MyRedisConStr"];
            options.InstanceName = "SampleInstance";
        });
    }

    services.AddRazorPages();
}

В приведенном выше коде предполагается, что основная строка подключения (StackExchange.Redis) была сохранена в конфигурации с именем MyRedisConStrключа.

См. сведения на странице с ценами на Azure Cache for Redis.

См. эту проблему GitHub для обсуждения альтернативных подходов к локальному кэшу Redis.

Распределенный кэш NCache

NCache — это распределенный кэш с открытым кодом в памяти, разработанный изначально в .NET и .NET Core. NCache работает как локально, так и настроен как кластер распределенного кэша для приложения ASP.NET Core, работающего в Azure или на других платформах размещения.

Сведения об установке и настройке NCache на локальном компьютере см. в руководстве по началу работы с Windows (.NET и .NET Core).

Чтобы настроить NCache, выполните приведенные действия.

  1. Установите NuGet с открытым кодом NCache.

  2. Настройте кластер кэша в client.ncconf.

  3. Добавьте следующий код в файл Startup.ConfigureServices:

    services.AddNCacheDistributedCache(configuration =>    
    {        
        configuration.CacheName = "demoClusteredCache";
        configuration.EnableLogs = true;
        configuration.ExceptionsEnabled = true;
    });
    

Использование распределенного кэша

Чтобы использовать IDistributedCache интерфейс, запросите экземпляр IDistributedCache из любого конструктора в приложении. Экземпляр предоставляется путем внедрения зависимостей (DI).

При запуске IDistributedCache примера приложения внедряется в Startup.Configure. Текущее время кэшируется с помощью IHostApplicationLifetime (дополнительные сведения см. в разделе Generic Host: IHostApplicationLifetime):

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    IHostApplicationLifetime lifetime, IDistributedCache cache)
{
    lifetime.ApplicationStarted.Register(() =>
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    });

Пример приложения внедряется IDistributedCache на IndexModel страницу "Индекс".

При каждой загрузке страницы индекса кэш проверяется на время кэширования.OnGetAsync Если срок действия кэшированного времени не истек, отображается время. Если 20 секунд прошло с момента последнего доступа к кэшированному времени (после последней загрузки этой страницы), на странице отображается срок действия кэшированного времени.

Немедленно обновите кэшированное время до текущего времени, нажав кнопку сброса кэшированного времени . Кнопка активирует OnPostResetCachedTime метод обработчика.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string CachedTimeUTC { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

Примечание

Время жизни экземпляров IDistributedCache (по крайней мере, для встроенных реализаций) не обязательно должно быть ограничено одним объектом или блоком.

Кроме того, вы можете создать IDistributedCache экземпляр, где бы он ни нуждался вместо использования внедрения зависимостей, но создание экземпляра в коде может усложнить тестирование и нарушение принципа явных зависимостей.

Рекомендации

При принятии IDistributedCache решения о том, какая реализация лучше всего подходит для вашего приложения, учитывайте следующее:

  • Существующая инфраструктура
  • Требования к производительности
  • Стоимость
  • Взаимодействие с командой

Решения кэширования обычно используют хранилище в памяти для быстрого извлечения кэшированных данных, но память является ограниченным ресурсом и дорогостоящим расширением. Храните только часто используемые данные в кэше.

Как правило, кэш Redis обеспечивает более высокую пропускную способность и меньшую задержку, чем кэш SQL Server. Однако для определения характеристик производительности стратегий кэширования обычно требуется тестирование производительности.

Если SQL Server используется в качестве хранилища резервного копирования распределенного кэша, использование одной и той же базы данных для кэша и обычного хранилища данных приложения и извлечения может отрицательно повлиять на производительность обоих. Рекомендуется использовать выделенный экземпляр SQL Server для хранилища резервного копирования распределенного кэша.

Дополнительные ресурсы