Làm việc với Distributed Cache trong ASP.NET Core

Trong bài viết trước tôi đã giới thiệu tới các bạn về In-memory cache trong ASP.NET Core. Các bạn có thể xem lại ở đây: https://viblo.asia/p/in-memory-caching-trong-aspnet-core-aWj53XmoK6m

Để có thể cải thiện hơn về hiệu suất và khả năng mở rộng của ứng dụng ASP.NET Core chúng ta có thể cần đến Distributed Cache (Cache phân tán). Bài viết này giải thích cách làm thế nào để làm việc với distributed cache được xây dựng sẵn trong ASP.NET Core.

1. Distributed Cache là gì ?

Distributed cache được chia sẻ bởi nhiều app servers. Thông tin trong cache không được lưu trong bộ nhớ của một web server riêng biệt và dự liệu được cache là có sẵn trong tất cả các server của ứng dụng. Điều này cung cấp một vài ưu điểm:

  • Cached data được liên lạc trên tất cả các web servers. Người dùng không thấy sự khác biệt kết quả cho dù web server nào xử lý request của họ.
  • Cached data vẫn tồn tại khi server restart và deployments. Một web server riêng biệt có thể gỡ bỏ hoặc thêm mới vào mà không ảnh hưởng đến cache.
  • Kho dữ liệu (database) có ít request được làm (so với multiple in-memory caches hoặc không cache).

Giống như bất kì loại cache nào, distributed cahe có thể cải thiện một cách đáng kể khả năng phản hồi của ứng dụng, từ đó data có thể được nhận từ cache sẽ nhanh hơn nhiều so với relational database (hoặc web service). Bài viết này miêu tả làm thế nào để cấu hình cả Redis và SQL Server distributed caches. Bất kể loại nào được chọn, ứng dụng tương tác với cache sử dụng một common IDistributedCache interface.

2. IDistributedCache Interface

IDistributedCache interface bao gồm các phương thức synchronous và asynchronous. Interface cho phép các items được thêm, nhận và gỡ bỏ khỏi distributed cache. IDistributedCache interface bao gồm những methods bên dưới:

Get, GetAsync

Với tham số là một key kiểu string và nhận về một item đã được cache như một mảng byte[] nếu được tìm thấy trong cache

Set, SetAsync

Thêm một item (mảng byte[]) tới cache sử dụng một string key.

Refresh, RefreshAsync

Làm mới một item trong cache dựa trên key của nó, đặt lại thời hạn của timeout

Remove, RemoveAsync

Gỡ bỏ một item cache dựa trên key của nó.

Để sử dụng IDistributedCache interface:

  • Thêm NuGet packages cần thiết tới project file của bạn.
  • Cấu hình implementation chỉ định của IDistributedCache trong method ConfigureServices trong Startuop class của bạn và thêm nó vào container
  • Từ Middleware hoặc MVC controller của ứng dụng, yêu cầu một instance của IDistributedCache từ constructor. Instance sẽ được cung cấp bởi Dependency Injection (DI).

Ví dụ bên dưới trình bày cách làm thế nào để sử dụng của IDistributedCache trong một thành phần middleware đơn giản:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DistCacheSample
{
    public class StartTimeHeader
    {
        private readonly RequestDelegate _next;
        private readonly IDistributedCache _cache;

        public StartTimeHeader(RequestDelegate next,
           IDistributedCache cache)
        {
            _next = next;
            _cache = cache;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            string startTimeString = "Not found.";
            var value = await _cache.GetAsync("lastServerStartTime");
            if (value != null)
            {
                startTimeString = Encoding.UTF8.GetString(value);
            }

            httpContext.Response.Headers.Append("Last-Server-Start-Time", startTimeString);

            await _next.Invoke(httpContext);
        }
    }


    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class StartTimeHeaderExtensions
    {
        public static IApplicationBuilder UseStartTimeHeader(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<StartTimeHeader>();
        }
    }
}

Trong code trên, giá trị đã cache được đọc, nhưng không bao giờ được ghi. Trong ví dụ này, giá trị chỉ được set khi một server là start up và không thay đổi. Trong kịch bản nhiều server, server gần đây nhất để bắt đầu sẽ ghi đè bất kì giá trị mà đã được thiết lập bởi các server khác. Phương thức Get và Set sử dụng kiểu byte[]. Vì thế, giá trị string phải được chuyển đổi sử dụng Encoding.UTF8.GetString (for Get)Encoding.UTF8.GetBytes (for Set).

Code bên dưới từ Startup.cs biểu diễn giá trị được thiết lập:

public void Configure(IApplicationBuilder app,
    IDistributedCache cache)
{
    var serverStartTimeString = DateTime.Now.ToString();
    byte[] val = Encoding.UTF8.GetBytes(serverStartTimeString);
    var cacheEntryOptions = new DistributedCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromSeconds(30));
    cache.Set("lastServerStartTime", val, cacheEntryOptions);

3. Sử dụng Redis Distributed Cache

Redis là một database open-source in-memory, cái mà thường xuyên được sử dụng như một distributed cache. Bạn có thể sử dụng nó tại local và bạn cũng có thể cấu hình một Azure Redis Cache cho những ứng dụng Azure-hosted ASP.NET Core. Ứng dụng ASP.NET Core cấu hình cache implementation sửu dụng một RedisDistributedCache instance.

Bạn cấu hình Redis implementation trong phương thức ConfigureServices và truy cập nó trong ứng dụng của bạn bởi việc yêu cầu một instance của IDistributedCache (Xem code bên dưới).

Trong code ví dụ, một RedisCache implementation được sử dụng khi server được cấu hình cho môi trường Staging. Như vậy phương thức ConfigureServices cấu hình RedisCache:

/// <summary>
/// Use Redis Cache in Staging
/// </summary>
/// <param name="services"></param>
public void ConfigureStagingServices(IServiceCollection services)
{

    services.AddDistributedRedisCache(options =>
    {
        options.Configuration = "localhost";
        options.InstanceName = "SampleInstance";
    });
}

4. Sử dụng một SQL Server Distributed Cache

SqlServerCache implementtation cho phép distributed cache để sử dụng một database SQL Server như một nơi lưu trữ của nó. Để tạo bảng SQL Server, bạn có thể sử dụng công cụ sql-cache, công cụ tạo một bảng với tên và schema bạn chỉ định. Để sử dụng công cụ sql-cache, thêm ``SqlConfig.Toolstới phần tử<ItemGroup>của file .csproj và chạydotnet restore```

<ItemGroup>
  <DotNetCliToolReference Include="Microsoft.Extensions.Caching.SqlConfig.Tools" Version="1.0.0-msbuild3-final" />
</ItemGroup

KIểm tra công cụ ```SqlConfig.Tools`` bởi chạy lệnh bên dưới:

C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create --help

Công cụ sql-cache sẽ hiển thị sử dụng, options và command hỗ trợ, bây giờ bạn có thể tạo bảng tới sql server, chạy lệnh sql-cache create:

C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create "Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache
   info: Microsoft.Extensions.Caching.SqlConfig.Tools.Program[0]
       Table and index were created successfully.

Đây là kết quả của lệnh trên:

Like all cache implementations, your app should get and set cache values using an instance of IDistributedCache, not a SqlServerCache. The sample implements SqlServerCache in the Production environment (so it is configured in ConfigureProductionServices).

Giống như tất các cache implementations , ứng dụng của bạn nên get và set giá trị cache sử dụng một instance của ``IDistributedCache, không phải SqlServerCache. Ví dụ triển khaiSqlServerCache``` trong môi trường Production (như vậy nó được cấu hình trong ).

/// Use SQL Server Cache in Production
/// </summary>
/// <param name="services"></param>
public void ConfigureProductionServices(IServiceCollection services)
{

    services.AddDistributedSqlServerCache(options =>
    {
        options.ConnectionString = @"Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;";
        options.SchemaName = "dbo";
        options.TableName = "TestCache";
    });

}

5. Kết

Khi quyết định implementation nào của IDistributed là đúng cho ứng dụng của bạn, lựa chọn giữa Redis và SQL Server được dựa trên cơ sở hạ tầng và môi trường đã có của bạn, yêu cầu về hiệu suất, và kinh nghiệm của team. Nếu team của bạn là làm việc thoải mái hơn với Redis, nó là một lựa chọn tuyệt vời. Nếu team bạn thích SQL Server, bạn có thể tự tin với implementation đó. Chú ý rằng giải pháp caching truyền thống lưu trữ data in-memory cái mà cho phép lấy dữ liệu nhanh. Bạn nên lưu trữ dữ liệu thường được sử dụng trong cache và lưu trữ toàn bộ dữ liệu trong một kho lưu trữ phụ trợ lâu dài như SQL Server hoặc Azure. Redis cache là một giải pháp caching cái mà mang lại cho bạn năng suất cao và độ trễ thấp so với SQL Cache.

Tham khảo