文档章节

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

十七宝宝
 十七宝宝
发布于 2017/05/31 15:31
字数 743
阅读 64
收藏 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王...

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

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

jinjiang2009
2017/03/14
0
0
缓存穿透、缓存击穿和缓存雪崩实践

我们使用缓存的主要目是提升查询速度和保护数据库等稀缺资源不被沾满。而缓存最常见的问题是缓存穿透、击穿和雪崩,在高并发下这三种情况都会有大量请求落到数据库,导致数据库资源沾满,引起...

xiaolyuh
2018/12/28
0
0
电商那些年,我摸爬打滚出的高并发架构实战精髓

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

Yomut
2016/10/10
391
1
Azure Redis Cache (1) 入门

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

zting科技
2017/10/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

定时获取服务器时间戳的一个类(Typescript)

export class TimeStampService { private _local_timestamp: number; // 本地时间戳 private _server_timestamp: number; // 服务器端时间戳 private _duration: number = 1......

lilugirl
21分钟前
0
0
前段技术总结

前端UI框架组件库: 说到前端框架我第一印象中想起React、Vue和Angular,不知道你是否与我一样想到这些,现在常用的有:Bootstrap、jQuery UI、BootMetro、AUI常用的还有很多、就不一一跟大家...

WinkJie
40分钟前
0
0
对话亲历者|鲁肃:我在支付宝“拧螺丝“的日子

摘要: 他是支付宝技术平台的奠基人之一,但是他总说“这还不是我心中最完美的架构”;他行事低调但却有着“此时此地,非我莫属”的豪气;他曾无数次充当救火大队长,但自评只是“没有掉队的...

阿里云云栖社区
48分钟前
4
0
设置 npm yarn 淘宝源

设置npm config set chromedriver_cdnurl=http://cdn.npm.taobao.org/dist/chromedriver设置yarn config set "chromedriver_cdnurl" "https://npm.taobao.org/mirrors/chromedriver"......

internetafei
57分钟前
2
0
Docker搭建Mysql集群、主从同步复制

1、创建数据挂载点: mkdir /opt/mysql-master/mysql、/opt/mysql-master/conf.d、/opt/mysql-slave/mysql、/opt/mysql-slave/conf.d 2、分别在master、slave节点文件目录conf.d下创建touch......

WALK_MAN
今天
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部