设为首页 收藏本站
查看: 898|回复: 0

[经验分享] AOP+memcached无侵入式集成

[复制链接]
累计签到:2 天
连续签到:1 天
发表于 2015-11-18 13:37:26 | 显示全部楼层 |阅读模式
  通常为了减轻数据库的压力和提升系统性能我们会将数据缓存至memcached中,传统的写法冗余而且高度耦合,本文试图通过Annotation+AOP无侵入式集成memcached。效果如下:
  

@Override
@ReadCacheAnnotation(clientName = CacheClientEnum.commodityclient, assignCacheKey = "${param0}", localExpire = 120)
public CommodityStyleVO getCommodityCacheModel(String commodityNo) {
return this.commodityMapper.getCommodityCacheModel(commodityNo);
}@Override
@UpdateCacheAnnotation(clientName = CacheClientEnum.commodityclient, assignCacheKey = "${param0}")
public CommodityStyleVO updateCommodityCacheModel(String commodityNo) {
return this.commodityMapper.updateCommodityCacheModel(commodityNo);
}仅仅通过一个注解就完美集成了memcached,而且如此透明灵活!!!
  不要高兴太早,这里有一个很重要的问题需要解决:我们都知道memcached通过key-value值对的形式来存储数据的,如何指定key是集成memcached关键所在,而注解中(annotation)根本无法根据当前的入参植入一个动态key,怎么办?借鉴freemarker一些思想我们可以先在注解中定义表达式模板,然后利用java反射机制来产生动态key,这个完全可以做到,不是吗?


  既然要定义表达式模板,我们需要制定表达式规则,举个例子:
  

@ReadCacheAnnotation(clientName = CacheClientEnum.cmsclient,
assignCacheKey = "brandslist+${param1.header(appversion)}+${param0.brand}")
public String brandSearch(CatBrandSearchVO searchVo,HttpServletRequest request){
return null;
}上面的assignCacheKey表达式将被解析成:  
  assignCacheKey = "brandslist"+request.getHeader("appversion")+searchVo.getBrand()
  看到到这里,我想大家都明白Annotation+AOP无侵入式集成memcached的原理了吧。
  


  在spring中使用Annotation+AOP进行增强仅需少量的代码即可实现,关于注解和AOP相关的基础知识这里不展开讨论。
  我们这里只是简单贴出核心代码段,以便有更清晰的认识:
  (1)ReadCacheAdvice - 环绕增强 ReadCacheAnnotation


  

@Aspect
@Component
public class ReadCacheAdvice extends CacheBase {
@Pointcut("@annotation(com.yougou.mobilemall.framework.cache.ReadCacheAnnotation)")
public void methodCachePointcut() {
}
@Around("methodCachePointcut()")
public Object methodCacheHold(final ProceedingJoinPoint joinPoint) throws Throwable {
ReadCacheAnnotation annotation =null;
IMemcachedCache memcachedCache = null;
Object result = null;
String cacheKey;
try {
// 获取目标方法
final Method method = this.getMethod(joinPoint);
annotation = method.getAnnotation(ReadCacheAnnotation.class);
memcachedCache = this.cacheManager.getCache(annotation.clientName().name());
// 是否启用本地缓存
cacheKey = this.getCacheKey(joinPoint.getArgs(), annotation.assignCacheKey());
if (annotation.localExpire() > 0) {
result = memcachedCache.get(cacheKey, annotation.localExpire());
} else {
result = memcachedCache.get(cacheKey);
}
if (result != null) {
return result;
}
} catch (Throwable ex) {
logger.error("Caching on " + joinPoint.toShortString() + " aborted due to an error.", ex);
return joinPoint.proceed();
}
// 缓存命中失败,执行方法从DB获取数据
result = joinPoint.proceed();
try {
// 将数据缓存到缓存服务器
if (result != null) {
if(annotation.remoteExpire()>0){
memcachedCache.put(cacheKey, result,annotation.remoteExpire());
}else{
memcachedCache.put(cacheKey, result);
}
}
} catch (Throwable ex) {
logger.error("Caching on " + joinPoint.toShortString() + " aborted due to an error.", ex);
}
return result;
}
}
(2)getCacheKey() - 利用表达式模板和java反射机制产生动态key  

/**
* @param method
* @param assignCacheKey
* @return
* @throws IllegalArgumentException
*/
protected String getCacheKey(Object[] args, String cacheKeyExpression) throws NoSuchMethodException,
IllegalArgumentException {
if (cacheKeyExpression == null || cacheKeyExpression.trim().equals("")) {
logger.error("This assignCacheKey is not valid on a method.");
throw new IllegalArgumentException("This assignCacheKey is not valid on a method.");
}
// 解析assignCacheKey表达式,格式如: ${param0}+ hello + ${param1.name(key)}
StringBuffer sbCacheKey = new StringBuffer(128);
String[] params = cacheKeyExpression.replaceAll(" ", "").split("[+]");
for (int i = 0; i < params.length; i++) {
if (params == null || &quot;&quot;.equals(params.trim())) {
continue;
}
Pattern pattern = Pattern.compile(&quot;^([$][{]).*[}]$&quot;);
Matcher matcher = pattern.matcher(params);
if (matcher.find()) {
// 根据参数获取参数值:${coupon.name}
String param = params.substring(2, params.length() - 1);
sbCacheKey.append(this.getArguValue(args, param));
} else {
sbCacheKey.append(params);
}
}
return Md5Encrypt.md5(sbCacheKey.toString());
}
/**
* 根据参数名获取参数值
*
* @param args
* @param param
* @return
* @throws IllegalArgumentException
* @throws NoSuchMethodException
*/
private String getArguValue(Object[] args, String params) throws NoSuchMethodException, IllegalArgumentException {
String[] arrParam = params.split(&quot;[.]&quot;);
if (arrParam[0] == null || &quot;&quot;.equals(arrParam[0])) {
logger.error(&quot;This assignCacheKey is not valid on a method.&quot;);
new IllegalArgumentException(&quot;This assignCacheKey is not valid on a method.&quot;);
}
// 方法入参列表中匹配当前参数对象
int index = Integer.parseInt(arrParam[0].replaceAll(&quot;param&quot;, &quot;&quot;));
Object currObject = args[index];
try {
for (int i = 1; i < arrParam.length; i++) {
// 根据参数获取参数值:name(key)
String param=arrParam;
Pattern pattern = Pattern.compile(&quot;([(]).*[)]$&quot;);
Matcher matcher = pattern.matcher(param);
if (matcher.find()) {
String paramName = param.substring(0, param.indexOf('('));
String paramKey = param.substring(param.indexOf('(')+1, param.length() - 1);
currObject = BeanUtils.getMappedProperty(currObject, paramName, paramKey);
} else {
currObject = BeanUtils.getProperty(currObject, param);
}
}
} catch (Exception ex) {
logger.error(&quot;This assignCacheKey is not valid on a method.&quot;);
new IllegalArgumentException(&quot;This assignCacheKey is not valid on a method.&quot;);
}
return currObject!=null? currObject.toString():&quot;&quot;;
}        我们的key是完全由使用者来决定的,这很大程度给予了使用者很大的自由性,这一点上我们甚至优于simple-spring-memcached,当然这也有一些弊端,我们无法在编译阶段对表达式模板进行验证,不熟悉表达式规则很容易出错。



  
  

版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-140749-1-1.html 上篇帖子: kestrel利用dubbo和memcached协议实现 队列服务 下篇帖子: 【源码剖析】tornado-memcached-sessions —— Tornado session 支持的实现(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表