缓存可以提高网站性能,减轻数据库压力。网站中常用的缓存分为业务数据缓存和页面文件缓存两类,其中业务数据缓存常用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;
}
}
}
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);
}
}
}
  • 缓存结果通知:实现了Spring.Aop中的IMethodInterceptor接口,用Spring的表达式解析得出key的值,使用时在方法上打特性标签,如Common.Mom项目中用到的[CacheResult(CacheName = "Aspnet", Key = "'Cmr.Dsr.GetSubscriber.' + #id", TimeToLive = "0:5:0")]
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即可

源码下载

作者: 张艺聍 发表于 2011-03-16 15:32 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架