ASP.NET Core Distributed Cache

Posted by Max Lin on 2023-02-11

如果說系統架構是多台的情況呢?例如:Load Balance,這時就適合用 分散式快取

簡單來說,分散式快取 就是統一將快取資料存在某個 儲存體,當多台機器去查詢資料時,就會指向該 儲存體 來進行存取。

這邊選用 Redis 來當作分散式快取的 儲存體

在實作前

  1. 先將 Redis 架在 localhost:6379 (架設可參考),並將連線字串加入 appsettings.json
  2. 將專案參考 Microsoft.Extensions.Caching.StackExchangeRedis

於 Program.cs 注入 IDistributedCache

如下:

// 取得 appsettings 中的 Redis 連線字串
var redisConStr = builder.Configuration.GetSection("RedisCache:ConnectionString").Value;
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = redisConStr;
});

在建構式傳入 IDistributedCache 實體

如下:

private readonly IDistributedCache _distributedCache;

public DistributedCacheController(IDistributedCache distributedCache)
{
    _distributedCache = distributedCache;
}

取得快取資料

如下:

var userId = 123;

// 取得快取資料,不存在則返回 null
var source = await _distributedCache.GetAsync(userId.ToString());
if (source == null)
{
    throw new KeyNotFoundException();        
}

var info = JsonSerializer.Deserialize<UserInfo>(source);

更新快取資料

如下:

var userId = 123;

await _distributedCache.SetStringAsync(
userId.ToString(), JsonSerializer.Serialize(info));

刪除快取資料

如下:

var userId = 123;
await _distributedCache.RemoveAsync(userId.ToString());

取得或新增快取資料

因為個人習慣使用 MemoryCache 的 GetOrCreate,所以也刻了一個擴充方法:

有關快取機制 AbsoluteSliding 於上一篇有介紹過了,讀者就根據使用情境來搭配使用

var userId = 123;
var cacheEntryOptions = new DistributedCacheEntryOptions
{
    // Set AbsoluteExpiration for 3 mins
    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(3),
    // Set SlidingExpiration for 5 s
    SlidingExpiration = TimeSpan.FromSeconds(5)
};

var info = await _distributedCache.GetOrCreateAsync(userId.ToString(), async () => await GetUserInfoFromDbAsync(), cacheEntryOptions);
}

擴充方法代碼如下:

public static async Task<TItem> GetOrCreateAsync<TItem>(this IDistributedCache cache, string key, Func<Task<TItem>> entity, DistributedCacheEntryOptions options = null)
{
    // 如果快取 options 沒傳入則自動設定預設快取
    if (options == null)
    {
        options = new DistributedCacheEntryOptions()
        {
            // 預設 5 分鐘快取
            AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5)
        };
    }

    // 取得快取資料,不存在則返回 null
    var value = await cache.GetStringAsync(key);
    
    // 不存在的情況
    if (value == null)
    {
        // 透過傳入的委派 entity 取得 resource
        var source = await entity();
        
        // 序列化 json
        var jsonSource = JsonSerializer.Serialize(source);
        
        // 存入快取並同時回傳
        await cache.SetStringAsync(key, jsonSource, options);
        return source;
    }
    
    return JsonSerializer.Deserialize<TItem>(value);
}

參考:

範例原始碼

ASP.NET Core中的分散式快取