文档章节

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

十七宝宝
 十七宝宝
发布于 2017/05/31 15:31
字数 743
阅读 24
收藏 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
大型站点高并发架构技术

大型站点高并发架构技术 高并发: 高并发主要是由于网站PV访问量大,单台服务器涌承载大量访问所带来的压力,所以会采用多台服务器进行分流,采用服务器集群技术,对于每个访问会被发送到哪台...

浮躁的码农
01/15
0
0
电商高并发架构实战精髓

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

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

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

Yomut
2016/10/10
391
1

没有更多内容

加载失败,请刷新页面

加载更多

Flask 开发填坑

插件的选择: flask-security 真的是个鸡肋啊。自带的页面,好丑。还不如用flask-login来做呢。

pearma
54分钟前
2
0
讲述下 :LVM逻辑卷管理遇到的问题

LVM学习逻辑卷管理创建逻辑卷遇到的问题 1 实验环境 系统 内核 发行版本 CentOS 2.6.32-754.2.1.el6.x86_64 CentOS release 6.10 (Final) 由于是最小化安装没有xfs命令,yum安装如下包支持此...

linuxprobe16
今天
1
0
day95-20180922-英语流利阅读-待学习

Hey Jude 半个世纪传唱不衰的背后故事 毛西 2018-09-22 1.今日导读 2004 年,The Beatles 被《滚石》杂志选为“历史上最伟大的 50 位流行音乐家的第一位”。这四名来自英国利物浦的男孩不仅对...

飞鱼说编程
今天
3
0
OSChina 周六乱弹 —— 放假前期焦虑症晚期

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @andonny :分享Matteo的单曲《Panama》: 《Panama》- Matteo 手机党少年们想听歌,请使劲儿戳(这里) @新垣吉衣OSC :我发现只要去有小朋友...

小小编辑
今天
325
10
wait()被notify()后,接着执行wait()后面的语句

wait()被notify()后,接着执行wait()后面的语句

noteman
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部