文档章节

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

十七宝宝
 十七宝宝
发布于 2017/05/31 15:31
字数 743
阅读 18
收藏 0
点赞 0
评论 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
杭州
程序员
《亿级流量网站架构核心技术》目录一览

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

jinjiang2009 ⋅ 2017/03/14 ⋅ 0

常用缓存系统使用经验总结

缓存系统是提升系统性能和处理能力的利器,常用的缓存系统各自的特性和使用场景有所不同,这里总结下不同缓存系统的实现方式、使用场景、使用缓存系统时需要关注的点,以及业务中缓存系统的选...

zqrferrari ⋅ 2017/10/26 ⋅ 0

大型站点高并发架构技术

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

浮躁的码农 ⋅ 01/15 ⋅ 0

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

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

Yomut ⋅ 2016/10/10 ⋅ 1

原腾讯云架构师分享可扩展系统的入门基础

肖立鹏原来在腾讯云做架构师,目前在Udesk担任CTO职位。下面是他分享的Udesk在可扩展高并发架构方面的一些实践经验: Udesk,是移动互联时代的企业级客服平台,采用SaaS的方式为企业提供基于...

mmlidong ⋅ 2015/05/22 ⋅ 0

电商高并发架构实战精髓

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

jinjiang2009 ⋅ 2016/10/10 ⋅ 0

Azure Redis Cache (1) 入门

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

zting科技 ⋅ 2017/10/04 ⋅ 0

大型分布式网站架构:缓存在分布式系统中的应用

缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。 一、缓存概述 缓存是分布式系统中的重要组件,主要解决高并发,大数据场...

烂猪皮 ⋅ 04/26 ⋅ 0

亿级流量电商详情页系统的大型高并发与高可用缓存架构实战

对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中最常见的一种应用技术。然而,行业里很多朋友对缓存技术的了解与掌握,仅仅停留在掌握redis/memca...

登录404 ⋅ 2017/06/05 ⋅ 0

大话程序猿眼里的高并发

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

oschina ⋅ 2016/05/17 ⋅ 23

没有更多内容

加载失败,请刷新页面

加载更多

下一页

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 36分钟前 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

版本控制工具

CSV , SVN , GIT ,VSS

颖伙虫 ⋅ 昨天 ⋅ 0

【2018.06.19学习笔记】【linux高级知识 13.1-13.3】

13.1 设置更改root密码 13.2 连接mysql 13.3 mysql常用命令

lgsxp ⋅ 昨天 ⋅ 0

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部