使用PostSharp实现Redis Cache的缓存自动读取

由于数据库负载过高,查看发现Oracle中保持连接的Session达到600+,为了降低数据库的负载,添加了Redis缓存处理。想着尽量不改变原有的代码结构,于是一直在想能不能写一个attribute,实现了该attribute的方法走Redis缓存路线。

  1. 实现思路:使用PostSharp获取方法的签名信息,扩展出针对方法的Attribute,添加此Attribute,方法调用时自动从缓存中读取,如果有值,直接返回,否则连接数据库获取数据。
  2. 具体方法包括两个class,一个用来获取方法的签名信息,并根据此信息读取Redis缓存,另一个是Redis缓存的处理逻辑。话不多说,上代码。
  • MethodTraceAttribute 用来获取签名信息以及Entry和Exit处理。
[Serializable]
    public sealed class MethodTraceAttribute : OnMethodBoundaryAspect
    {

        public MethodTraceAttribute()
        {
        }
        public string SignInfo { get; set; }
        public void SetMethodSign(MethodExecutionArgs args)
        {
            SignInfo = string.Format("Method:{0}.{1}",
                                        args.Method.DeclaringType.Name,
                                        args.Method.Name);
            SignInfo += ",Paras={";
            for (int x = 0; x < args.Arguments.Count; x++)
            {
                var para = args.Method.GetParameters()[x].Name + ":" +
                                args.Arguments.GetArgument(x);
                SignInfo += para;
            }
            SignInfo += "}";
            var type = (args.Method as MethodInfo).ReturnType.ToString();
            SignInfo += ",Return=" + type;
        }
        public override void OnEntry(MethodExecutionArgs args)
        {
            base.OnEntry(args);
            SetMethodSign(args);
            
            var mthInfo = args.Method as MethodInfo;
            var cacheValue = RedisCache.GetCacheValue<string>(SignInfo);
            if (cacheValue != null)
            {
                if (mthInfo != null)
                    args.ReturnValue = JsonConvert.DeserializeObject(cacheValue, mthInfo.ReturnType);
                else
                    args.ReturnValue = null;
                args.FlowBehavior = FlowBehavior.Return;
            }
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            var cacheValue = RedisCache.GetCacheValue<string>(SignInfo);
            if (cacheValue == null)
                RedisCache.SetCacheValue<string>(SignInfo, JsonConvert.SerializeObject(args.ReturnValue),args.Method.Name);
        }
    }

SetMethodSign获取方法的命名空间+类名+参数声明+参数值+方法返回类型,作为方法的唯一签名。当参数值不同时,签名肯定不同,返回类型不同,签名也不同。

OnEntry 在方法进入时,先获取方法签名,以方法签名作为Key获取Redis中的缓存,如果存在,将缓存值设置为返回值ReturnValue,然后FlowBehavior = FlowBehavior.Return,直接返回,否则方法按照原来设定继续访问数据库。

OnExit 在方法退出时,根据签名信息获取Redis缓存,如果没有,将返回值添加至Redis缓存。

  • RedisCache 类用来获取和设置Redis缓存信息,为了便于设置,添加了CacheSetUp.config配置文件。

RedisCache 类代码:

public class RedisCache
    {
        private static RedisCache curcache=null;
        public static ConfigXmlDocument conf = null;
        private readonly ConnectionMultiplexer redisConnections;

        public static RedisCache GetCurCache()
        {
            if (curcache == null)
                curcache = new RedisCache();
            return curcache;
        }
        public ConfigXmlDocument GetCacheSetUpConfig()
        {
            if (conf == null)
            {
                var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CacheSetUp.config");
                conf = new ConfigXmlDocument();
                conf.Load(filepath);
            }
            return conf;
        }
        public RedisCache()
        {
            var con = GetRedisConf();
            if (con == null)
                redisConnections = null;
            else
                redisConnections = ConnectionMultiplexer.Connect(con);

        }
        public string GetConfigValue(string key)
        {
            try
            {
                var ele = GetCacheSetUpConfig().SelectSingleNode("//appSettings").SelectSingleNode("//add[@key='" + key + "']") as XmlElement;
                if (ele == null) return "";
                return ele.GetAttribute("value");
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
        public ConfigurationOptions GetRedisConf()
        {
            if (GetConfigValue("RedisCacheEnable") == "N")
                return null;
            var configstr = GetConfigValue("DefaultConnect");
            return ConfigurationOptions.Parse(configstr);
        }
        public static void SetCacheValue<T>(object cachekey,T value,string configName="Dfault") where T : class
        {
            if (GetCurCache().redisConnections == null) return;
            var key = JsonConvert.SerializeObject(cachekey);
            GetCurCache().Set<T>(key,value, GetConfigTimeOut(configName));
        }
        public static TimeSpan GetConfigTimeOut(string configName)
        {
            var timespan = new TimeSpan();
            string key = "DefaultTimeOut";
            var speckey = configName + "TimeOut";
            if (curcache.GetConfigValue(speckey) != "")
                key = speckey;
            var t = curcache.GetConfigValue(key).Split(',').ToList();
            timespan = new TimeSpan(int.Parse(t[0]), int.Parse(t[1]), int.Parse(t[2]));
            return timespan;
        }
        public static T GetCacheValue<T>(object cachekey) where T : class
        {
            var key = JsonConvert.SerializeObject(cachekey);
            return GetCurCache().Get<T>(key);
        }

        public T Get<T>(string key) where T : class
        {
            if (curcache.redisConnections == null) return null;
            var db = curcache.redisConnections.GetDatabase();
            var redisObject = db.StringGet(key);
            if (redisObject.HasValue)
            {
                return JsonConvert.DeserializeObject<T>(redisObject
                        , new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
                            PreserveReferencesHandling = PreserveReferencesHandling.Objects
                        });
            }
            else
            {
                return (T)null;
            }
        }
        public void Set<T>(string key, T objectToCache, TimeSpan? expiretime=null) where T : class
        {
            
            var db = curcache.redisConnections.GetDatabase();
            db.StringSet(key, JsonConvert.SerializeObject(objectToCache
                        , Newtonsoft.Json.Formatting.Indented
                        , new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
                            PreserveReferencesHandling = PreserveReferencesHandling.Objects
                        }), expiretime);
        }


        
    }

RedisCache 类没啥好讲的,从Redis获取缓存,反序列化为特定类型。同时附上CacheSetUp.config配置文件信息,配置文件可以设置缓存是否开启,同时也可以针对特定方法设置特定的缓存期限。默认30分钟。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="RedisCacheEnable" value="Y"/>
    <add key="DefaultTimeOut" value="0,30,0"/>
    <add key="DefaultConnect" value="127.0.0.1:6379,serviceName=25,connectTimeout=100000,abortConnect=False,connectRetry=3,responseTimeout=2800000"/>
  </appSettings>
</configuration>

猜你喜欢

转载自blog.csdn.net/weixin_42757710/article/details/81146281