[英]Redis Cache cashe-aside pattern code issue
我正在嘗試創建一個使用 Redis 緩存來構建以下模式的 BaseService:
我一切正常,但由於某種原因,調用以獲取結果的服務方法在編譯之前需要“等待等待”。 我似乎無法弄清楚為什么我的 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.