簡體   English   中英

Redis 緩存緩存模式代碼問題

[英]Redis Cache cashe-aside pattern code issue

我正在嘗試創建一個使用 Redis 緩存來構建以下模式的 BaseService:

  1. 從緩存中獲取
  2. 如果在緩存中,則返回結果
  3. 如果結果是 null,調用 Func 從源(數據庫)獲取結果
  4. 放入緩存
  5. Func 的返回結果

我一切正常,但由於某種原因,調用以獲取結果的服務方法在編譯之前需要“等待等待”。 我似乎無法弄清楚為什么我的 ResultFromCache 方法被雙重包裝,該方法旨在模仿 Ok() 在 WebAPI 中的作用。 你能幫我找到我沒有返回正確結果的地方嗎,這樣我的這種模式的用戶就不必使用兩次等待來獲得他們的結果:)

這是我的代碼的精簡版本,需要在 GetMessage 方法中等待 await。

using StackExchange.Redis;
using System.Text.Json;

namespace TestCache
{
    public class Service: BaseService
    {
        //Injected DbContextx
        private Context context { get; }

        //service method
        public async Task<Message> GetMessage(int accountId)
        {
            return await await ResultFromCache(accountId, FetchMessage, "Message");
        }

        //get from database method
        private async Task<Message> FetchMessage(int parentId)
        {
            //Example of using EF to retrieve one record from Message table
            return await context.Message;
        }
    }

    public class BaseService
    {
        private const int Hour = 3600;

        private ConnectionMultiplexer connection;
        private IDatabaseAsync database;

        public async Task<T1> ResultFromCache<T1, T2>(T2 param1, Func<T2, T1> fromSource, string cacheKey, int cacheDuration = Hour)
        {
            //get from cache
            var result = await CacheGet<T1>(cacheKey);

            if (result != null)
                return result;

            //get from db
            result = fromSource(param1);

            //TODO: add to cache
            return result;
        }

        public async Task<T> CacheGet<T>(string key)
        {
            var value = await database.StringGetAsync(key);

            if (value.IsNull)
            {
                return default;
            }

            return JsonSerializer.Deserialize<T>(value);
        }
    }
}

正如我在評論中提到的,您的fromSource需要定義為Func<T2, Task<T1> ,因為您需要等待 func。

但是,關於在ResultFromCache中檢查 null,您還有一個微妙的錯誤。 正如目前所寫,如果在CacheGet<T>中返回默認值,值類型將錯誤地返回。 要解決這個問題,您需要使用`EqualityComparer.Default.Equals 來檢查默認值,而不是簡單地使用 null。

public class BaseService
{
    private const int Hour = 3600;

    private ConnectionMultiplexer connection;
    private IDatabaseAsync database;

    public async Task<T1> ResultFromCache<T1, T2>(T2 param1, Func<T2, Task<T1>> fromSource, 
        string cacheKey, int cacheDuration = Hour)
    {
        //get from cache
        var result = await CacheGet<T1>(cacheKey);

        if (!EqualityComparer<T1>.Default.Equals(result, default(T1)))
            return result;

        //get from db
        result = await fromSource(param1);

        //TODO: add to cache
        return result;
    }

    public async Task<T> CacheGet<T>(string key)
    {
        var value = await database.StringGetAsync(key);

        if (value.IsNull)
        {
            return default;
        }

        return JsonSerializer.Deserialize<T>(value);
    }
}

最后,如果多個請求同時遇到 Redis 緩存未命中,它們都會調用您的加載器 function。如果查詢開銷很大,這可能會給您的加載源帶來很大的壓力。 一個常見的解決方案是雙重檢查鎖定,它可以作為SemaphoreSlim實例的 ConcurrentDictionary 來實現,或者在 Stephen Cleary 的AsyncDuplicateLock中有更好的實現: 基於鍵的異步鎖定

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM