利用Attribute与PIAB构建支持memcached功能的数据访问层

之前一直在做memcached的研究,并通过memcached做了很多性能优化,把许多执行频繁的语句 通过cache缓存起来,但做的很多都是service层的复杂数据缓存,数据访问层基本没有碰过。如果要动数据访问层,假设按照最普通的编程思路,说不 定我们改造数据访问层,可能会产生以下这样的代码:

class UserDAC
{
public User GetUser(int id)
{
//从缓存中取
User user = memcached.get(user_”+id)
 
If(user == null)
{
//没取到从缓存中取
user = db.LoadFromKey(user_table”,id);
 
//存入缓存中
if(user != null)
memcached.add(user_”+id,user);
}
 
//返回结果
return user;
}
}

其实这个函数的核心逻辑是 user = db.LoadFromKey(…),如果能把cache的存取方法给剥离出去,就能达到解耦的目的。而实现这一目的的方式就是用AOP的办法来实现。

AOP的实现方式有很多,不使用语言特性的话可以利用装饰者模式来实现类似AOP的效果。我这为了图方便,推荐大家使用企业库里的Policy Injection Application Block(PIAB),而且这块实现的功能也比装饰者模式强大很多。

基本的思路是这样的:

1.    让UserDAC类继承MarshalByRefObject:这个基类是支持远程处理的对象基类,学过远程对象调用的同志们都知道,他会生成代理类来支持实际对象的调用。PIAB实现的办法就是在这个代理类上做了手脚。

2.    用特别的方法来构造UserDAC对象:如果用UserDAC dac = new UserDAC(),那这个还没动手脚的对象是实现不了AOP的,需要通过特殊的方式来构造对象。具体方式如下:

UserDAC dac = PolicyInjection.Create<UserDAC>();

这样拿到的对象就可以让我们为所欲为了,哇哈哈哈….不好意思…有点兴奋了…

3.    设置拦截policy:这里可以通过企业库自带的配置程序来生成config,其中拦截方法有两种方式:

a)    可以通过正则表达式匹配来选择拦截哪些方法,不过这种办法比较麻烦,因为都是写在config里,到时候config长的恐怖。

b)    我这边使用的是attribute来实现拦截,这样默认情况下只要拦截所有的方法就行,我通过attribute的判断来决定是否拦截。

这边attribute定义如下:

[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
public string CacheName
{   get; set}
 
public string Operation
{   get; set;   }
 
public CacheAttribute(string cacheName, string operation)
{ …  }
 
}

如果某个方法上加了这个attribute,我们就认为他是被拦截方法,执行cache过程。其中的operation是个枚举,代表cache的get,set,update,remove等操作,默认是get&set操作。

那我们的UserDAC可以定义这样了:

class UserDAC : MarshalByRefObject
{
[Cache(user”, ”get)]
public User GetUser([CachePara]int id)
{
return db.LoadFromKey(user_table”,id);
}
 
[Cache(user”, “update)]
public bool Update([CachePara]int id, User user)
{
db.update(user);
}
}

大家可以看到通过PIAB和attribute的双重方法,支持缓存的数据访问层的代码变得和没有缓存支持的代码差不多了,无非是多了几个attribute,不需要再去写那么多冗余的memached的get,set,remove操作了。
其中大家可以看到有个[CachPara]的attribute,这个是用来标明cache的key的,如果cachename相同,cachepara对应的值也相同,那我们就认为是一个对象。

最后我们还有一件事情要做,就是要自己写个自定义的handler来处理拦截的事件,由于篇幅限制,我就写下伪代码,为大家理清思路为主:

[ConfigurationElementType(typeof(CustomCallHandlerData))]
class CacheHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
//通过反射找方法是否有CacheAtrribute
CacheAttribute att = getAtt(cacheattribute”, input);
 
//没有,执行原方法并返回结果
If(att == null) return getNext();
 
//根据不同的操作,来做不同的事情
switch(att.operation)
{
//读取操作
case get&set:
//先读memcached
object cacheobj = memcache.get(cachename+cachpara);
//有的话直接返回
if(cacheobj != null)
return cacheobj;
else
{
//没有就执行原方法,并把值存入缓存
object returnobj = getnext();
memcached.add(cachename+cachepara,returnobj);
return returnobj;
}
//更新操作
case update:
//清除缓存,避免数据不同步
memcache.update(cachename+cachepara)
return getnext();
}
 
}
}

通过这样一个简单方便的改造,我们就可以很容易构建带memcached功能的数据访问层。而且即使你不想用缓存,直接读数据库,那也很简单,用new UserDAC()对象来调用方法即可,使用相当灵活。

呵呵,可能写的有点乱,如果大家有不理解的可以回复本文,我会及时回答的。

 

原文地址:http://it.dianping.com/using_attribute_and_piab_to_hybrid_memcached_and_data_access_layer.htm

posted @ 2008-10-24 13:05  Figo Yang  阅读(1600)  评论(1编辑  收藏  举报