基于Couchbase和Spring.Net AOP的分布式缓存实现



1引言

2分布式缓存Couchbae

2.1安装

先从官网下载最新的安装程序,有两个版本:企业版和社区版,官网上有说明这两个版本的区别,可以按需下载,我们下载了企业版。

安装很简单,一路下一步就可以了。安装完成后会自动打开浏览器,打开登录页面,URLhttp://localhost:8091,大家只要记好端口号就可以了,登录地址都是http://服务器IP:8091

第一次登录界面如下:

 

2.2配置

2.2.1初始配置

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

这一步很得要,设置管理员的帐户和密码,帐户可以使默认,输入密码即可,这个密码要记好,以后登陆后台管理页面时都要用到这个密码。

 

 

到这里初始配置就完成了。

接下来,我们开始具体应用配置。

2.2.2配置Bucket

打开浏览器,输入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,服务器配置完成,可以开始使用。

2.3测试

2.3.1客户端程序集

1到官网http://www.couchbase.com/download

下载开发.net程序所需要的客户端程序集

 

现在下载到Couchbase-Net-Client-1.3.3,解压后,有两个文件夹:net35net40,用net40文件里的程序集

 

 

2.3.2代码测试

新建一个控制台应用程序,在项目属性中,应用程序=》目标框架下拉框中选择“.NET Framework 4”,不要用默认的“.NET Framework 4 Client Profile”。

给项目添加引用,引用上文下载的Couchbase Net Client,要应用如下程序集:Couchbase.dllEnyim.Caching.dllEnyim.Caching.Log4NetAdapter.dllllog4net.dllNewtonsoft.Json.dll

Enyim.Caching.Log4NetAdapter.dlllog4net.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时输入的密码,然后再将uriIP换成你的服务器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进行封装。

 

 

3Spring.Net AOP在项目中的实现

由于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

}

 

3.1用特性实现缓存的精确控制。

如果我门要对一个方法的返回值进行缓存,那么要确定两个问题:缓存类型和缓存时间。为了解决这个问题,可以用特性来实现。如果某个方法的返回值需要被缓存,只要在方法上加特性就可以了。

 

    /// <summary>

    /// 缓存特性,服务方法加上这个提醒后,将对返回值进行缓存

    /// </summary>

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)]

    public class CacheAttribute : Attribute

    {

        /// <summary>

        /// 缓存天数

        /// </summary>

        public uint Day { getset; }

 

        /// <summary>

        /// 缓存类型

        /// </summary>

        public CacheType CacheType { getset; }

 

        public CacheAttribute()

        {

            Day = 1;

            CacheType = CacheType.Distribution;

        }

    }

 

使用方式如下:

 

Day=3表示要缓存3天,CacheType=CacheType.Distribution表示使用分布式缓存。

 

3.2AOP环绕通知

当方法被截获时,先获取方法的特性,从特性中获取缓存的时间和缓存类型,

先读取缓存,如果缓存存中有数据,则直接返回缓存中的数据,不在执行方法,

如果缓存中没有数据,则先执行方法,然后将方法的返回值缓存后再返回方法的值。

其中有一个重要的问题需要处理:当方法的参数为应用类型时,如参数表明 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;

    }

}

 

 

3.3AOP配置

 

<!--通过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"/>

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值