上一篇談了我對(duì)緩存的概念,框架上的理解和看法,這篇承接上篇講講我自己的緩存模塊設(shè)計(jì)實(shí)踐。
基本的緩存模塊設(shè)計(jì)
最基礎(chǔ)的緩存模塊一定有一個(gè)統(tǒng)一的CacheHelper,如下:
public interface ICacheHelper { T Get<T>(string key); void Set<T>(string key, T value); void Remove(string key); }然后業(yè)務(wù)層是這樣調(diào)用的
public User Get(int id) { if (id <= 0) throw new ArgumentNullException("id"); var key = string.Format(USER_CACHE_KEY, id); var user = _cacheHelper.Get<User>(key); if (user != null) return user; return _repository.Get(id); } 上面的代碼沒什么錯(cuò)誤,但是實(shí)際運(yùn)用的時(shí)候就產(chǎn)生疑問(wèn)了,因?yàn)槲乙恢睆?qiáng)調(diào)緩存要保存"熱數(shù)據(jù)",那樣"熱數(shù)據(jù)"一定會(huì)有過(guò)期的時(shí)候,我們不可能另外寫一個(gè)去Set。所以干脆就結(jié)合到一起寫是比較合適的。
public User GetV2(int id){ if (id <= 0) throw new ArgumentNullException("id"); var key = string.Format(USER_CACHE_KEY, id); var user = _cacheHelper.Get<User>(key); if (user != null) return user; user = _repository.Get(id); if (user != null) _cacheHelper.Set(key, user); return user;}上面的代碼其實(shí)只是加了一個(gè)Set而已,就這樣的設(shè)計(jì)的話,每次一個(gè)Get需要的重復(fù)代碼實(shí)在是太多了,那么是不是應(yīng)該更精簡(jiǎn)?這時(shí)候吃點(diǎn)C#語(yǔ)法糖就很有必要了,語(yǔ)法糖偶爾吃點(diǎn)增進(jìn)效率,何樂(lè)而不為?
public User GetV3(int id){ if (id <= 0) throw new ArgumentNullException("id"); var key = string.Format(USER_CACHE_KEY, id); return _cacheHelperV2.Get<User>(key, () => _repository.Get(id)); }//ICache Get<T>實(shí)現(xiàn)public T Get<T>(string key, Func<T> fetch = null){ T result = default(T); var obj = Cache.Get(key); if (obj is T) { result = (T)obj; } if(result == null) { result = fetch(); if (result != null) Set(key, result); } return result;} 這里我直接把Set方法都包裝進(jìn)了ICache.Get<T>,附帶上Fetch Func。這樣就把公共的操作抽象到了一起,簡(jiǎn)化了Cache的調(diào)用,完美的符合了我的想法。
緩存模塊設(shè)計(jì)進(jìn)階
上一節(jié)里的ICache V3幾乎已經(jīng)最精簡(jiǎn)了,但是其實(shí)參考了ServiceStack.Redis之后,我發(fā)現(xiàn)了更加的抽象方式。很明顯上一節(jié)的所有代碼里,都是手動(dòng)管理Key的,對(duì)于通常的對(duì)象Cache,這個(gè)Key還需要手動(dòng)嗎?來(lái)上最后一份改進(jìn)。
public T Get<T>(object id, Func<T> fetch = null){ var type = typeof(T); var key = string.Format("urn:{1}:{2}", type.Name, id.ToString());//這里是關(guān)鍵,直接用TypeName來(lái)充當(dāng)Key return Get(key, fetch);}public T Get<T>(string key, Func<T> fetch = null){ T result = default(T); var obj = Cache.Get(key); if (obj is T) { result = (T)obj; } if (result == null) { result = fetch(); if (result != null) Set(key, result); } return result;}
新聞熱點(diǎn)
疑難解答
圖片精選