目录
目录I
1 引言1
2 分布式缓存Couchbae1
2.1 安装1
2.2 配置1
2.3 测试7
3 Spring.Net AOP在项目中的实现10
3.1 用特性实现缓存的精确控制。10
3.2 AOP环绕通知11
3.3 AOP配置14
先从官网下载最新的安装程序,有两个版本:企业版和社区版,官网上有说明这两个版本的区别,可以按需下载,我们下载了企业版。
安装很简单,一路下一步就可以了。安装完成后会自动打开浏览器,打开登录页面,URL:http://localhost:8091,大家只要记好端口号就可以了,登录地址都是http://服务器IP:8091。
第一次登录界面如下:
Step 1 of 5 CONFIGURE SERVER
配置数据保存路径和最大可用内存
点击“Next”,
Step 2 of 5 SMPLE BUCKETS
CREATE DEFAULT BUCKET Step 3 of 5
SAMPLE BUCKETS 是例子,可以不管,直接一步
“CREATE DEFAULT BUCKE”可以用默认,直接下一步,以后这些设置都是可以修改的。
Step 4 of 5 NOTIFICATIONS
使用默认,点击“Next”
Step 5 of 5 CONFIGURE SERVER
这一步很得要,设置管理员的帐户和密码,帐户可以使默认,输入密码即可,这个密码要记好,以后登陆后台管理页面时都要用到这个密码。
到这里初始配置就完成了。
接下来,我们开始具体应用配置。
打开浏览器,输入http://主机IP:8091,或者http://主机名:8091,如:http://ddc-grape:8091,页面打开后会出现登录页面
输入账户和密码
进入主页面后,按如下操作
在弹窗“Create Bucket”按如下填写
Bucket Settings
Bucket Name中输入 “grape”,Bucket Type 选择“Couchbase”,
Memory Size
Per Node Ram Quota填写600,这个可以根据主机所拥有的内存填写,
Access Control
选择Sandard port (TCP port 11211. Needs SASL auth.)
Enter password输入框中输入密码,后边访问缓存时需要用到。
其他的配置可以用默认值,然后点击“Create”按钮。
至此,我们已经创建了一个Bucket,服务器配置完成,可以开始使用。
1到官网http://www.couchbase.com/download
下载开发.net程序所需要的客户端程序集
现在下载到Couchbase-Net-Client-1.3.3,解压后,有两个文件夹:net35和net40,用net40文件里的程序集
新建一个控制台应用程序,在项目属性中,应用程序=》目标框架下拉框中选择“.NET Framework 4”,不要用默认的“.NET Framework 4 Client Profile”。
给项目添加引用,引用上文下载的Couchbase Net Client,要应用如下程序集:Couchbase.dll,Enyim.Caching.dll,Enyim.Caching.Log4NetAdapter.dlll,log4net.dll,Newtonsoft.Json.dll,
Enyim.Caching.Log4NetAdapter.dll和log4net.dll主要用于记录日志,Newtonsoft.Json.dll用于将对象转换为JSON格式的数据。
配置文件
给项目添加一个应用程序配置文件App.config,内容如下
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
</configSections>
<couchbase>
<servers bucket="grape" bucketPassword="kotei$88">
<add uri="http://192.168.5.58:8091/pools"/>
</servers>
</couchbase>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
需要注意的是server节点,buket填上文新建Bucket时输入的名称,bucketPassword填上文新建Bucket时输入的密码,然后再将uri的IP换成你的服务器IP,配置就完成了。
写代码测试
参考文档:
http://www.couchbase.com/communities/net
http://docs.couchbase.com/couchbase-sdk-net-1.3/#getting-started
static void Main(string[] args)
{
var client = new CouchbaseClient();
client.Store(StoreMode.Set, "123", "abcdefg");
string value = client.Get<string>("123");
Console.WriteLine(value);
Console.ReadKey(true);
}
如果能正常打印出“abcdefg”,则表示配置正常。
对Couchbase进行封装。
由于Grape项目中的代码已经基本稳定,如果直接在原代码逻辑中加入缓存代码,会第原代码结构造成破坏,所以采用Spring.Net 的AOP来实现缓存。
由于ASP.NET中已经有了Session缓存和Application缓存,再加上分布式缓存,一共有三个缓存,所以为了方便使用,首先新建一个枚举。
public enum CacheType : int
{
/// <summary>
/// 分布式缓存,全局缓存,
/// 此缓存可用于多台WEB服务器,底层用Couchbase实现。
/// </summary>
Distribution = 0,
/// <summary>
/// Asp.Net应用程序级缓存,全局缓存,
/// 此缓存只能用于单台WEB服务器。
/// </summary>
Application,
/// <summary>
/// 回话及缓存
/// </summary>
Session
}
如果我门要对一个方法的返回值进行缓存,那么要确定两个问题:缓存类型和缓存时间。为了解决这个问题,可以用特性来实现。如果某个方法的返回值需要被缓存,只要在方法上加特性就可以了。
/// <summary>
/// 缓存特性,服务方法加上这个提醒后,将对返回值进行缓存
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)]
public class CacheAttribute : Attribute
{
/// <summary>
/// 缓存天数
/// </summary>
public uint Day { get; set; }
/// <summary>
/// 缓存类型
/// </summary>
public CacheType CacheType { get; set; }
public CacheAttribute()
{
Day = 1;
CacheType = CacheType.Distribution;
}
}
使用方式如下:
Day=3表示要缓存3天,CacheType=CacheType.Distribution表示使用分布式缓存。
当方法被截获时,先获取方法的特性,从特性中获取缓存的时间和缓存类型,
先读取缓存,如果缓存存中有数据,则直接返回缓存中的数据,不在执行方法,
如果缓存中没有数据,则先执行方法,然后将方法的返回值缓存后再返回方法的值。
其中有一个重要的问题需要处理:当方法的参数为应用类型时,如参数表明 out ref 时,参数也是返回值,需要特殊处理。
代码如下
/// <summary>
/// AOP环绕通知
/// </summary>
public class AopAroundAdvise : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
object retVal;
var methodAttrs = invocation.Method.GetCustomAttributes(typeof(CacheAttribute), false);
//如果特性不对,则直接调用返回
if(methodAttrs == null || methodAttrs.Length != 1)
{
return invocation.Proceed();
}
CacheAttribute cacheAttr = (CacheAttribute)methodAttrs[0];
TimeSpan time = TimeSpan.FromDays(cacheAttr.Day);
CacheType cacheType = cacheAttr.CacheType;
//如果没有启用分布式缓存
//if (cacheType == CacheType.Distribution &&
// !ConfigManager.Instance.IsDistributionCacheAvailable)
//{
// return invocation.Proceed();
//}
ICacheService cacheService = CacheService.Instance;
Type targeType = invocation.TargetType;
string methodName = invocation.Method.Name;
//引用类型参数
//<参数名,参数位置>
IDictionary<string,int> dicRefArgs = new Dictionary<string,int>();
ParameterInfo[] paras = invocation.Method.GetParameters();
foreach(var p in paras)
{
if (p.ParameterType.IsByRef)
{
dicRefArgs.Add(p.Name,p.Position);
}
}
//用作参数,防止缓存Key重名
object[] args = null;
if (paras.Length > 0)
{
args = (object[])invocation.Arguments.Clone();
}
//从缓存中获取数据
retVal = cacheService.Get(cacheType,methodName,args,targeType);
//如果有缓存
if(retVal != null)
{
//从缓存获取引用类型参数的数据
if(dicRefArgs.Count > 0)
{
foreach(var p in dicRefArgs)
{
invocation.Arguments[p.Value] = cacheService.Get(cacheType,
string.Format("{0}@{1}",methodName,p.Key),args,targeType);
}
}
//直接返回,不调用原来方法
return retVal;
}
//---------------------------------------------------------//
//调用目标方法
retVal = invocation.Proceed();
//缓存数据给下次使用
cacheService.Add(cacheType, methodName, args, retVal, time, targeType);
//缓存引用类型参数的数据
if (dicRefArgs.Count > 0)
{
foreach (var p in dicRefArgs)
{
cacheService.Add(cacheType,
string.Format("{0}@{1}", methodName, p.Key), args, invocation.Arguments[p.Value], time, targeType);
}
}
return retVal;
}
}
<!--通过AOP实现缓存-->
<!--参考http://www.cnblogs.com/GoodHelper/archive/2009/11/16/SpringNet_Aop_Config.html-->
<object id="proxyFactoryObject" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
<property name="ObjectNames">
<list>
<!--代理以Serverice结尾的类-->
<value>*Service</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<!--引用切面-->
<value>attributeMatchAdvisor</value>
</list>
</property>
</object>
<!--切面-->
<object id="attributeMatchAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
<!--引用自己写的环绕通知-->
<property name="Advice" ref="aroundAdvice"/>
<!--自己写的特性,有这个特性的方法将被截获-->
<property name="Attribute" value="Kotei.Grape.Common.Data.CacheAttribute, Kotei.Grape.Common" />
</object>
<!--自己写的环绕通知-->
<object id ="aroundAdvice" type="Kotei.Grape.Service.AopAroundAdvise, Kotei.Grape.Service"/>