[转载].Net通用基础框架(.Net Common Infrastructure)-缓存 – 张艺聍 – 博客园.
缓存可以提高网站性能,减轻数据库压力。网站中常用的缓存分为业务数据缓存和页面文件缓存两类,其中业务数据缓存常用AspnetCache,Memcached等,而页面文件缓存常用Squid和Nginx,今天 介绍的内容是业务数据缓存。
- Common.Cache类图
- 缓存接口ICache:使用Add方法时,如果key存在,则返回false。使用Set方法时,key不存在则添加,否则更新。
using System; using System.Collections.Generic; namespace Common.Cache { /// <summary> /// 缓存 /// </summary> public interface ICache { /// <summary> /// 增加 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">键</param> /// <param name="value">值</param> /// <returns>结果</returns> bool Add<T>(string key, T value); /// <summary> /// 增加 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">键</param> /// <param name="value">值</param> /// <param name="duration">持续时间</param> /// <returns>结果</returns> bool Add<T>(string key, T value, TimeSpan duration); /// <summary> /// 清除 /// </summary> void Clear(); /// <summary> /// 获取 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">键</param> /// <returns>值</returns> T Get<T>(string key); /// <summary> /// 多线程获取 /// </summary> /// <param name="keys">键集合</param> /// <returns>值集合</returns> IDictionary<string, object> MultiGet(IList<string> keys); /// <summary> /// 移除 /// </summary> /// <param name="key">键</param> void Remove(string key); /// <summary> /// 设置 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">键</param> /// <param name="value">值</param> /// <returns>结果</returns> bool Set<T>(string key, T value); /// <summary> /// 设置 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">键</param> /// <param name="value">值</param> /// <param name="duration">持续时间</param> /// <returns>结果</returns> bool Set<T>(string key, T value, TimeSpan duration); } }
缓存基类
using System; using System.Collections.Generic; namespace Common.Cache { /// <summary> /// 缓存基类 /// </summary> public abstract class CacheBase : ICache { private TimeSpan maxDuration = TimeSpan.FromDays(15); /// <summary> /// 最长持续时间 /// </summary> public TimeSpan MaxDuration { get { return this.maxDuration; } set { this.maxDuration = value; } } /// <summary> /// 前缀 /// </summary> public string Prefix { get; set; } public bool Add<T>(string key, T value) { return this.Add<T>(key, value, this.MaxDuration); } public abstract bool Add<T>(string key, T value, TimeSpan duration); public abstract void Clear(); public abstract T Get<T>(string key); /// <summary> /// 获取全名 /// </summary> /// <param name="key">键</param> /// <returns>全名</returns> public virtual string GetFullName(string key) { string result = key; if (!string.IsNullOrWhiteSpace(this.Prefix)) { result = string.Format("{0}.{1}", this.Prefix, key); } return result; } public abstract IDictionary<string, object> MultiGet(IList<string> keys); public abstract void Remove(string key); public bool Set<T>(string key, T value) { return this.Set<T>(key, value, this.MaxDuration); } public abstract bool Set<T>(string key, T value, TimeSpan duration); } }
Aspnet缓存实现
using System; using System.Collections; using System.Collections.Generic; using System.Web; using System.Web.Caching; namespace Common.Cache { /// <summary> /// Aspnet缓存 /// </summary> public class AspnetCache : CacheBase { private System.Web.Caching.Cache cache = HttpRuntime.Cache; /// <summary> /// 构造函数 /// </summary> public AspnetCache() : this("Common.Cache") { } /// <summary> /// 构造函数 /// </summary> /// <param name="prefix">前缀</param> public AspnetCache(string prefix) { this.Prefix = prefix; } public override bool Add<T>(string key, T value, TimeSpan duration) { bool result = false; if (value != null) { if (duration <= TimeSpan.Zero) { duration = this.MaxDuration; } result = this.cache.Add(this.GetFullName(key), value, null, DateTime.Now.Add(duration), System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Default, null) == null; } return result; } public override void Clear() { // 获取键集合 IList<string> keys = new List<string>(); IDictionaryEnumerator caches = this.cache.GetEnumerator(); while (caches.MoveNext()) { string key = caches.Key.ToString(); if (key.StartsWith(this.Prefix)) { keys.Add(key); } } // 移除全部 foreach (string key in keys) { this.cache.Remove(key); } } public override T Get<T>(string key) { T result = default(T); object value = this.cache.Get(this.GetFullName(key)); if (value is T) { result = (T)value; } return result; } public override IDictionary<string, object> MultiGet(IList<string> keys) { IDictionary<string, object> result = new Dictionary<string, object>(); foreach (string key in keys) { result.Add(key, this.Get<object>(key)); } return result; } public override void Remove(string key) { this.cache.Remove(this.GetFullName(key)); } public override bool Set<T>(string key, T value, TimeSpan duration) { bool result = false; if (value != null) { if (duration <= TimeSpan.Zero) { duration = this.MaxDuration; } this.cache.Insert(this.GetFullName(key), value, null, DateTime.Now.Add(duration), System.Web.Caching.Cache.NoSlidingExpiration); result = true; } return result; } } } [/csharp] Memcached分布式缓存实现:EnyimMemcached客户端中实现了一致性哈希算法 [csharp] using System; using System.Collections.Generic; using System.Linq; using Enyim.Caching; using Enyim.Caching.Memcached; namespace Common.Cache { /// <summary> /// Memcached缓存 /// </summary> public class MemcachedCache : CacheBase { private static MemcachedClient memcached = new MemcachedClient(); public override bool Add<T>(string key, T value, TimeSpan duration) { if (duration <= TimeSpan.Zero) { duration = this.MaxDuration; } return memcached.Store(StoreMode.Add, this.GetFullName(key), value, duration); } public override void Clear() { memcached.FlushAll(); } public override T Get<T>(string key) { return memcached.Get<T>(this.GetFullName(key)); } public override IDictionary<string, object> MultiGet(IList<string> keys) { IEnumerable<string> fullKeys = keys.Select<string, string>(k => this.GetFullName(k)); return memcached.Get(fullKeys); } public override void Remove(string key) { memcached.Remove(this.GetFullName(key)); } public override bool Set<T>(string key, T value, TimeSpan duration) { if (duration <= TimeSpan.Zero) { duration = this.MaxDuration; } return memcached.Store(StoreMode.Set, this.GetFullName(key), value, duration); } } } [/csharp] 缓存结果通知:实现了Spring.Aop中的IMethodInterceptor接口,用Spring的表达式解析得出key的值,使用时在方法上打特性标签,如Common.Mom项目中用到的[CacheResult(CacheName = "Aspnet", Key = "'Cmr.Dsr.GetSubscriber.' + #id", TimeToLive = "0:5:0")] [csharp] using System.Collections; using System.Collections.Generic; using System.Reflection; using AopAlliance.Intercept; using Spring.Caching; using Spring.Context; using Spring.Expressions; namespace Common.Cache.Aspects { /// <summary> /// 缓存结果通知 /// </summary> public class CacheResultAdvice : IApplicationContextAware, IMethodInterceptor { private IDictionary<MethodInfo, CacheResultAttribute> cacheResults = new Dictionary<MethodInfo, CacheResultAttribute>(); public IApplicationContext ApplicationContext { get; set; } /// <summary> /// 获取缓存结果 /// </summary> /// <param name="invocation">调用</param> /// <returns>缓存结果</returns> private CacheResultAttribute GetCacheResult(IMethodInvocation invocation) { CacheResultAttribute result = null; MethodInfo method = invocation.Method; if (this.cacheResults.ContainsKey(method)) { result = this.cacheResults[method]; } if (result == null) { object[] attributes = method.GetCustomAttributes(typeof(CacheResultAttribute), false); if (attributes.Length > 0) { result = (CacheResultAttribute)attributes[0]; this.cacheResults[method] = result; } } return result; } /// <summary> /// 获取参数集合 /// </summary> /// <param name="invocation">调用</param> /// <returns>参数集合</returns> private IDictionary GetParameters(IMethodInvocation invocation) { IDictionary result = new Hashtable(); MethodInfo method = invocation.Method; object[] arguments = invocation.Arguments; ParameterInfo[] parameters = method.GetParameters(); for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameter = parameters[i]; result[parameter.Name] = arguments[i]; } return result; } public object Invoke(IMethodInvocation invocation) { object result = null; IDictionary parameters = this.GetParameters(invocation); CacheResultAttribute cacheResult = this.GetCacheResult(invocation); if (cacheResult != null && cacheResult.KeyExpression != null) { string key = cacheResult.KeyExpression.GetValue(null, parameters).ToString(); if (!string.IsNullOrEmpty(key)) { ICache cache = this.ApplicationContext.GetObject(cacheResult.CacheName) as ICache; if (cache != null) { result = cache.Get<object>(key); if (result == null) { result = invocation.Proceed(); if (this.IsMatch(cacheResult.ConditionExpression, result, parameters)) { cache.Set<object>(key, result, cacheResult.TimeToLiveTimeSpan); } } } } } if (result == null) { result = invocation.Proceed(); } return result; } /// <summary> /// 是否匹配 /// </summary> /// <param name="expression">表达式</param> /// <param name="context">上下文</param> /// <param name="parameters">参数集合</param> /// <returns>结果</returns> private bool IsMatch(IExpression expression, object context, IDictionary parameters) { bool result = expression == null; if (!result) { result = (bool)expression.GetValue(result, parameters); } return result; } } }
测试时启动Common.Cache.ConsoleTest即可