緩(huan)存(cun)篇~第八(ba)回(hui) Redis實現基于方法簽名的數據集緩(huan)存(cun)~續(優化緩(huan)存(cun)中的key)
上一講主要是說如何將數據集存儲到redis服務器里,而今(jin)天主要說(shuo)的是緩存(cun)里的鍵名,我們(men)習慣叫它key.
redis或者其(qi)它緩存組(zu)件(jian)實現的(de)存儲機(ji)制(zhi)里(li),它將很多方(fang)(fang)法對應的(de)數據(ju)集存儲在(zai)一個(ge)公共的(de)空(kong)(kong)間(jian)里(li),這個(ge)空(kong)(kong)間(jian)足夠(gou)大,當然它也是共享(xiang)(xiang)的(de),沒有(you)具(ju)體的(de)分區,也就是說(shuo),如果你的(de)key重復了(le),那這事就有(you)點(dian)壞味道了(le),對于一個(ge)項目(mu)肯定(ding)沒什么問題,只(zhi)要做到方(fang)(fang)法名(ming)不相同(tong)就可以(yi),但是,如果是多個(ge)項目(mu)共享(xiang)(xiang)一個(ge)緩存服務器(緩存中間(jian)件(jian),這是很正常的(de),沒有(you)什么公司一個(ge)項目(mu)對應一個(ge)緩存服務器,沒必(bi)要,當你的(de)項目(mu)足夠(gou)大時,可以(yi)會有(you)分模塊(kuai)去(qu)做緩存服務器的(de)概念(nian)),今天的(de)重點(dian)就是在(zai)攔(lan)截(jie)組(zu)件(jian)中優(you)化我們的(de)key,對于get和put,remove方(fang)(fang)法都要進行優(you)化.
/// <summary> /// 表(biao)示(shi)用(yong)于方法緩存功(gong)能的攔(lan)截行為。 /// </summary> public class CachingBehavior : IInterceptionBehavior { /// <summary> /// 緩(huan)存(cun)項(xiang)目名稱(cheng),每個項(xiang)目有自己(ji)的名稱(cheng) /// 避免緩(huan)存鍵名(ming)重復 /// </summary> static readonly string cacheProjectName = System.Configuration.ConfigurationManager.AppSettings["CacheProjectName"] ?? "DataSetCache"; #region Private Methods /// <summary> /// 根據指定的<see cref="CachingAttribute"/>以及<see cref="IMethodInvocation"/>實例(li), /// 獲取與某一特(te)定參數值相關的鍵名。 /// </summary> /// <param name="cachingAttribute"><see cref="CachingAttribute"/>實例。</param> /// <param name="input"><see cref="IMethodInvocation"/>實例。</param> /// <returns>與某一特定參數值相關的鍵名。</returns> private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input) { switch (cachingAttribute.Method) { // 如果(guo)是Remove,則(ze)不存在(zai)特定(ding)值鍵名,所有的(de)以(yi)該方法名稱(cheng)相(xiang)關的(de)緩存都需要(yao)清(qing)除 case CachingMethod.Remove: return null; // 如果是Get或者Put,則需要產生一(yi)個針對(dui)特定(ding)參數值(zhi)的鍵名 case CachingMethod.Get: case CachingMethod.Put: if (input.Arguments != null && input.Arguments.Count > 0) { var sb = new StringBuilder(); for (int i = 0; i < input.Arguments.Count; i++) { if (input.Arguments[i].GetType().BaseType == typeof(LambdaExpression))//lambda處理 { var exp = input.Arguments[i] as LambdaExpression; var arr = ((System.Runtime.CompilerServices.Closure)(((System.Delegate)(Expression.Lambda(exp).Compile().DynamicInvoke())).Target)).Constants; Type t = arr[0].GetType(); string result = ""; foreach (var member in t.GetFields()) { result += member.Name + "_" + t.GetField(member.Name).GetValue(arr[0]) + "_"; } result = result.Remove(result.Length - 1); sb.Append(result.ToString()); } else if (input.Arguments[i].GetType() != typeof(string)//類和結構體(ti)處理 && input.Arguments[i].GetType().BaseType.IsClass) { var obj = input.Arguments[i]; Type t = obj.GetType(); string result = ""; foreach (var member in t.GetProperties())//公開屬性
{
result += member.Name + "_" + t.GetProperty(member.Name).GetValue(obj) + "_";
}
foreach (var member in t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))//私有和公用字段
{
result += member.Name + "_" + t.GetField(member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj) + "_";
} result = result.Remove(result.Length - 1); sb.Append(result.ToString()); } else//簡(jian)單值類型處理 { sb.Append(input.Arguments[i].ToString()); } if (i != input.Arguments.Count - 1) sb.Append("_"); } return sb.ToString(); } else return "NULL"; default: throw new InvalidOperationException("無效的緩存方式。"); } } #endregion #region IInterceptionBehavior Members /// <summary> /// 獲取當(dang)前行為需要攔截的對象類(lei)型接(jie)口(kou)。 /// </summary> /// <returns>所有需(xu)要攔截的對象類型接口。</returns> public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } /// <summary> /// 通過實現此方法來攔截(jie)調用并執行(xing)所需的(de)攔截(jie)行(xing)為。 /// </summary> /// <param name="input">調用攔截目(mu)標時的輸入信息。</param> /// <param name="getNext">通過行(xing)為鏈來獲(huo)取下一(yi)個攔截行(xing)為的(de)委(wei)托。</param> /// <returns>從(cong)攔截(jie)目標獲得的返回信息。</returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var method = input.MethodBase; //鍵值前綴 string prefix = cacheProjectName + "_" + input.Target.ToString() + "_"; //鍵名(ming),在put和get時(shi)使用 var key = prefix + method.Name; if (method.IsDefined(typeof(CachingAttribute), false)) { var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0]; var valKey = GetValueKey(cachingAttribute, input); switch (cachingAttribute.Method) { case CachingMethod.Get: try { if (CacheManager.Instance.Exists(key, valKey)) { var obj = CacheManager.Instance.Get(key, valKey); var arguments = new object[input.Arguments.Count]; input.Arguments.CopyTo(arguments, 0); return new VirtualMethodReturn(input, obj, arguments); } else { var methodReturn = getNext().Invoke(input, getNext); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Put: try { var methodReturn = getNext().Invoke(input, getNext); if (CacheManager.Instance.Exists(key)) { if (cachingAttribute.Force) { CacheManager.Instance.Remove(key); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Put(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Remove: try { var removeKeys = cachingAttribute.CorrespondingMethodNames; foreach (var removeKey in removeKeys) { string delKey = prefix + removeKey; if (CacheManager.Instance.Exists(delKey)) CacheManager.Instance.Remove(delKey); } var methodReturn = getNext().Invoke(input, getNext); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } default: break; } } return getNext().Invoke(input, getNext); } /// <summary> /// 獲(huo)取一個<see cref="Boolean"/>值,該值表示當前攔截行為被調用時,是否真的需要執行 /// 某些操作。 /// </summary> public bool WillExecute { get { return true; } } #endregion }
優化后的(de)key的(de)結構(gou)為(wei)項(xiang)目前(qian)綴_項(xiang)目命(ming)名(ming)空(kong)間_方法(fa)名(ming),這樣的(de)設計(ji)我(wo)想它不會再有重復了,事實上,如果你(ni)的(de)項(xiang)目正規的(de)話,只(zhi)要有(項(xiang)目命(ming)名(ming)空(kong)間_方法(fa)名(ming))這一(yi)層控(kong)制就(jiu)可以做到key值唯一(yi)了,而為(wei)了避免意外,我(wo)們還是加了一(yi)個項(xiang)目前(qian)綴CacheProjectName!
我們可以看一(yi)下緩存存儲時的鍵名(ming)截圖