设为首页 收藏本站
查看: 782|回复: 0

[经验分享] 缓存篇~第八回 Redis实现基于方法签名的数据集缓存~续(优化缓存中的key)

[复制链接]

尚未签到

发表于 2015-7-21 10:45:09 | 显示全部楼层 |阅读模式
  返回目录
  上一讲主要是说如何将数据集存储到redis服务器里,而今天主要说的是缓存里的键名,我们习惯叫它key.
  redis或者其它缓存组件实现的存储机制里,它将很多方法对应的数据集存储在一个公共的空间里,这个空间足够大,当然它也是共享的,没有具体的分区,也就是说,如果你的key重复了,那这事就有点坏味道了,对于一个项目肯定没什么问题,只要做到方法名不相同就可以,但是,如果是多个项目共享一个缓存服务器(缓存中间件,这是很正常的,没有什么公司一个项目对应一个缓存服务器,没必要,当你的项目足够大时,可以会有分模块去做缓存服务器的概念),今天的重点就是在拦截组件中优化我们的key,对于get和put,remove方法都要进行优化.



///
/// 表示用于方法缓存功能的拦截行为。
///
public class CachingBehavior : IInterceptionBehavior
{
///
/// 缓存项目名称,每个项目有自己的名称
/// 避免缓存键名重复
///
static readonly string cacheProjectName = System.Configuration.ConfigurationManager.AppSettings["CacheProjectName"] ?? "DataSetCache";

#region Private Methods
///
/// 根据指定的以及实例,
/// 获取与某一特定参数值相关的键名。
///
/// 实例。
/// 实例。
/// 与某一特定参数值相关的键名。
private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input)
{
switch (cachingAttribute.Method)
{
// 如果是Remove,则不存在特定值键名,所有的以该方法名称相关的缓存都需要清除
case CachingMethod.Remove:
return null;
// 如果是Get或者Put,则需要产生一个针对特定参数值的键名
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.GetType().BaseType == typeof(LambdaExpression))//lambda处理
                            {
var exp = input.Arguments 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.GetType() != typeof(string)//类和结构体处理
&& input.Arguments.GetType().BaseType.IsClass)
{
var obj = input.Arguments;
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//简单值类型处理
                            {
sb.Append(input.Arguments.ToString());
}
if (i != input.Arguments.Count - 1)
sb.Append("_");
}
return sb.ToString();
}
else
return "NULL";
default:
throw new InvalidOperationException("无效的缓存方式。");
}
}
#endregion
#region IInterceptionBehavior Members
///
/// 获取当前行为需要拦截的对象类型接口。
///
/// 所有需要拦截的对象类型接口。
public IEnumerable GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
///
/// 通过实现此方法来拦截调用并执行所需的拦截行为。
///
/// 调用拦截目标时的输入信息。
/// 通过行为链来获取下一个拦截行为的委托。
/// 从拦截目标获得的返回信息。
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var method = input.MethodBase;
//键值前缀
string prefix = cacheProjectName + "_" + input.Target.ToString() + "_";
//键名,在put和get时使用
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);
}
///
/// 获取一个值,该值表示当前拦截行为被调用时,是否真的需要执行
/// 某些操作。
///
public bool WillExecute
{
get { return true; }
}
#endregion
}
  优化后的key的结构为项目前缀_项目命名空间_方法名,这样的设计我想它不会再有重复了,事实上,如果你的项目正规的话,只要有(项目命名空间_方法名)这一层控制就可以做到key值唯一了,而为了避免意外,我们还是加了一个项目前缀CacheProjectName!
  我们可以看一下缓存存储时的键名截图

  返回目录
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88994-1-1.html 上篇帖子: Redis源码解析3 下篇帖子: redis介绍【转】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表