文档章节

高并发系统Redis缓存应用使用SpringAOP+Redis-Cache

十七宝宝
 十七宝宝
发布于 2017/05/31 15:31
字数 743
阅读 26
收藏 0

在中兴通讯工作期间做了一个WAP网站,压测要求很高,由于ORM框架使用的是ZTE内部的开发包,性能很差,所以在这个项目中使用了SpringAOP与Redis做了切面缓存,并在Nginx层做了大量缓存优化,成功支持3000QPS.

1.根据注解拦截实现缓存层细粒度控制,缓存失效时间动态从注解配置获取

2.两次缓存命中,提高缓存命中率

3.缓存失效,双重判断锁机制防止请求并发打入关系数据库

仍可优化点有三点:

1.优化KEY生成策略,覆盖所有类型入参格式

2.缓存生命周期控制更细粒度及动态化配置(使用枚举类型及开发缓存子系统)

3.部分缓存层与关系数据库强一致性实现

主要实现:

/**

* @Author Yang.Liu17

* @Version

* @Date 2017/2/9.

*/

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface Cache {

public int expire() default 0; //缓存多少秒,默认无限期

}

 

package com.jd.admin.AOP;

 

import com.alibaba.fastjson.JSON;

import com.jd.admin.Enum.Cache;

import org.apache.log4j.Logger;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.Signature;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.ValueOperations;

import org.springframework.stereotype.Component;

 

import java.lang.reflect.Method;

import java.util.concurrent.TimeUnit;

 

/**

* @Author Yang.Liu17

* @Version

* @Date 2017/2/9.

*/

@Component

@Aspect

public class CacheAspect {

 

public static final Logger logger = Logger.getLogger(CacheAspect.class);

 

/**

* 使用redisTemplate

*/

@Autowired

private RedisTemplate redisTemplate;

 

/**

* 定义分隔符

*/

private static final String DELIMITER=":";

 

 

/**

* 定义锁后缀

*/

private static final String LOCKSTR="_LOCKSTR";

 

/**

* 根据注解拦截

*/

@Pointcut("@annotation(com.jd.admin.Enum.Cache)")

public void controllerPointcut() {}

 

/**

* 获取或添加缓存

* @return

* @throws Throwable

*/

@Around("controllerPointcut()")

public Object RedisCache(final ProceedingJoinPoint joinPoint)

throws Throwable {

// 得到类名、方法名和参数

String clazzName = joinPoint.getTarget().getClass().getName();

String methodName = joinPoint.getSignature().getName();

ValueOperations<String, Object> redisOper = redisTemplate.opsForValue();

Object[] args = joinPoint.getArgs();

// 根据类名、方法名和参数生成Key

logger.info("key参数: " + clazzName + "." + methodName);

String key = getKey(clazzName, methodName, args);

if (logger.isInfoEnabled()) {

logger.info("生成key: " + key);

}

Object value = getCachedData(redisOper,key);

// result是方法的最终返回结果

Object result = null;

if (null == value) {

// 缓存未命中

if (logger.isDebugEnabled()) {

logger.debug("缓存未命中");

}

// 调用数据库查询方法

synchronized (key+LOCKSTR){

value = getCachedData(redisOper,key);

if(null==value){

result = joinPoint.proceed(args);

}

}

// 序列化查询结果

final String jsonResult = serialize(result);

final int keyExpire = getKeyExpire(joinPoint);

if(keyExpire==0){

redisOper.set(key, jsonResult);

}else{

// 序列化结果放入缓存

redisOper.set(key, jsonResult, keyExpire, TimeUnit.SECONDS);

}

return jsonResult;

 

}

 

// 缓存命中

if (logger.isDebugEnabled()) {

logger.debug("缓存命中, value = " + value);

}

 

// 反序列化从缓存中拿到的json

result = JSON.parse((String)value);

if (logger.isDebugEnabled()) {

logger.debug("反序列化结果 = {}" + result);

}

 

return result;

}

 

 

private int getKeyExpire(ProceedingJoinPoint joinPoint){

/**

* 获取目标方法上的注解

*/

Signature signature = joinPoint.getSignature();

MethodSignature methodSignature = (MethodSignature)signature;

Method targetMethod = methodSignature.getMethod();

Class clazz = targetMethod.getClass();

/**

* 获取方法上注解中表明的缓存生效时间

*/

Cache cache = targetMethod.getAnnotation(Cache.class);

int expire = cache.expire();

return expire;

}

 

/**

* 获取缓存数据,两次请求,增加缓存命中率

* @param key

* @return

*/

private Object getCachedData(ValueOperations<String, Object> redisOper,String key){

Object value = redisOper.get(key);

if(null == value){

value = redisOper.get(key);

}

return value;

}

 

 

/**

* 生成KEY的逻辑

* @param clazzName

* @param methodName

* @param args

* @return

*/

private String getKey(String clazzName, String methodName, Object[] args) {

StringBuilder key = new StringBuilder(clazzName);

key.append(DELIMITER);

key.append(methodName);

key.append(DELIMITER);

if(args.length>0){

for (Object obj : args) {

if(obj!=null){

key.append(obj.toString());

key.append(DELIMITER);

}

 

}

}

return key.toString();

}

/**

* 序列化,使用FastJSON

* @param target

* @return

*/

protected String serialize(Object target) {

return JSON.toJSONString(target);

}

 

}

© 著作权归作者所有

共有 人打赏支持
十七宝宝
粉丝 0
博文 12
码字总数 6864
作品 0
杭州
程序员
私信 提问
从Memcache转战Redis,聊聊缓存使用填过的“坑”

本文内容转自“51CTO技术栈”微信公众平台。 但有时候,即使使用了 Cache,却发现系统依然卡顿宕机,是因为 Cache 技术不好吗?非也,其实这是缓存的治理工作没有做好。 艺龙机票事业群CTO王...

壹佰案例
07/27
0
0
《亿级流量网站架构核心技术》目录一览

举报   在2011年年底的时候笔者就曾规划写一本Spring的书,但是因为是Spring入门类型的书,框架的内容更新太快,觉得还是写博客好一些,因此就把写完的书稿放到了博客(jinnianshilongnia...

jinjiang2009
2017/03/14
0
0
电商那些年,我摸爬打滚出的高并发架构实战精髓

一、关于高并发 高并发是指在同一个时间点,有很多用户同时访问URL地址,比如:淘宝的双11、双12,就会产生高并发。又如贴吧的爆吧,就是恶意的高并发请求,也就是DDOS攻击,再屌丝点的说法就...

Yomut
2016/10/10
391
1
电商高并发架构实战精髓

一、关于高并发 高并发是指在同一个时间点,有很多用户同时访问URL地址,比如:淘宝的双11、双12,就会产生高并发。又如贴吧的爆吧,就是恶意的高并发请求,也就是DDOS攻击,再屌丝点的说法就...

jinjiang2009
2016/10/10
0
0
Azure Redis Cache (1) 入门

 《Windows Azure Platform 系列文章目录》   Microsoft Azure Redis Cache基于流行的开源Redis Cache。   1.功能   Redis 是一种高级的键值存储,其中,键可以包含数据结构,例如字符...

zting科技
2017/10/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

闲话高并发的那些神话,看京东架构师如何把它拉下神坛

高并发也算是这几年的热门词汇了,尤其在互联网圈,开口不聊个高并发问题,都不好意思出门。高并发有那么邪乎吗?动不动就千万并发、亿级流量,听上去的确挺吓人。但仔细想想,这么大的并发与...

James-
8分钟前
0
0
Emacs 系列:让我们拥抱 Emacs 和 org 模式

导读 我必须承认,在使用了几十年的 vim 后, 我被 Emacs 吸引了。长期以来,我一直对如何组织安排事情感到沮丧。我也有用过 GTD 和 ZTD 之类的方法,但是像邮件或是大型文件这样的事务真的很...

问题终结者
9分钟前
0
0
解析Node.js通过axios实现网络请求

本次给大家分享一篇node.js通过axios实现网络请求的方法,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。 1、使用Npm 下载axios n...

前端攻城老湿
22分钟前
2
0
深入浅出之React-redux中connect的装饰器用法@connect

这篇文章主要介绍了react-redux中connect的装饰器用法@connect详解,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。 通常我们需要一...

前端攻城小牛
23分钟前
2
0
详解css BEM书写规范

BEM是基于组件的web开发方法。其思想是将用户界面分隔为独立的块,从而使开发复杂的UI界面变得更简单和快,且不需要粘贴复制便可复用现有代码。BEM由Block、Element、Modifier组成。选择器里...

前端小攻略
38分钟前
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部