结构包:

使用案例:

实现效果:

1、基本并发的本地缓存,基于分布式轻量级锁的redis缓存

2、热缓存(高频访问持续缓存)+快速过期(本地缓存2秒,redis缓存10秒)

3、方法级别缓存清理 (@HybridCache 与@HybridChange 绑定管理缓存 )

4、基于HybridType接口的可扩展式作用域,目前已实现:全局、token

5、基于HybridLevel接口的可扩展式缓存处理,目前已实现:本地缓存、redis缓存

核心代码包:

package com.*.server.live.core.hybridCache;

import com.*.server.live.core.hybridCache.impl.DepthLocal;
import com.*.server.live.core.hybridCache.impl.DepthRedis; import java.lang.annotation.*; /**
* 功能描述:多重缓存
* 作者:唐泽齐
* @case @HybridCache(scope = ScopeGlobal.class)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface HybridCache { /*缓存深度*/
Class<? extends HybridLevel> depth() default DepthRedis.class; /*缓存广度*/
Class<? extends HybridType> scope(); }

@HybridCache

package com.*.server.live.core.hybridCache;

import com.*.server.live.core.hybridCache.impl.DepthRedis;

import java.lang.annotation.*;

/**
* 功能描述:多重缓存更新
* 作者:唐泽齐
* @case @HybridChange(clazz = LiveStudioController.class,method = "getStudio",args = {Long.class})
* @case @HybridChange(clazz = LiveStudioController.class,method = "getStudio",args = {})
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface HybridChange { /*关联类*/
Class<? extends Object> clazz(); /*关联方法*/
String method(); /*关联方法入参类型*/
Class<?>[] args();
}

@HybridChange

package com.*.server.live.core.hybridCache;

import org.aspectj.lang.ProceedingJoinPoint;

import javax.validation.constraints.NotNull;

/**
* 功能描述:多重缓存级别
* 作者:唐泽齐
*/
public interface HybridLevel {
/**
* 缓存
* @param key 缓存key
* @param id 混村ID
* @param o 缓存值
* @param self 是否执行节点
* @param joinPoint 节点
* @return
* @throws Throwable
*/
Object cache(@NotNull String key,@NotNull String id, Object o,@NotNull boolean self,@NotNull ProceedingJoinPoint joinPoint) throws Throwable; /**
* 清缓存
* @param key 缓存key
* @param self 是否执行节点
* @param joinPoint 节点
* @return
* @throws Throwable
*/
Object del(@NotNull String key,@NotNull boolean self,@NotNull ProceedingJoinPoint joinPoint) throws Throwable;
}

HybridLevel

package com.*.server.live.core.hybridCache;

/**
* 功能描述:多重缓存级别
* 作者:唐泽齐
*/
public interface HybridType {
String token();
}

HybridType

package com.*.server.live.core.hybridCache;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.util.Md5Utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration; import javax.annotation.Resource;
import java.lang.reflect.Method; /**
* 功能描述:多重缓存接口
* 作者:唐泽齐
*/
@Aspect
@Configuration
public class HybridCacheInterceptor {
@Resource
ApplicationContext applicationContext;
final static String cache = "hybridCache:"; @Around(value = "@annotation(com.*.server.live.core.hybridCache.HybridCache)")
public Object cache(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
HybridCache cacheAnnotation = method.getAnnotation(HybridCache.class);
String key = getCacheKey(method);
String id = getCacheId(method,joinPoint.getArgs());
HybridLevel hybridLevel = applicationContext.getBean(cacheAnnotation.depth());
return hybridLevel.cache(key, id, null, true, joinPoint);
} @Around(value = "@annotation(com.*.server.live.core.hybridCache.HybridChange)")
public Object del(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
HybridChange cacheAnnotation = method.getAnnotation(HybridChange.class);
try {
Method method1 = cacheAnnotation.clazz().getMethod(cacheAnnotation.method(),cacheAnnotation.args());
HybridCache cache = method1.getAnnotation(HybridCache.class);
String key = getCacheKey(method1);
HybridLevel hybridLevel = applicationContext.getBean(cache.depth());
return hybridLevel.del(key,true, joinPoint);
} catch (Exception e) {
return joinPoint.proceed();
}
} /*获取缓存key*/
private String getCacheKey(Method method) {
return cache + method.getDeclaringClass().getSimpleName() + ":" + method.getName();
} ; /*获取缓存id*/
private String getCacheId(Method method,Object[] args) {
HybridCache cacheAnnotation = method.getAnnotation(HybridCache.class);
HybridType hybridType = applicationContext.getBean(cacheAnnotation.scope());
return Md5Utils.getMD5((hybridType.token() + JSON.toJSONString(args)).getBytes());
} ; }

HybridCacheInterceptor

扩展代码包:

package com.*.server.live.core.hybridCache.impl;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.*.server.live.core.hybridCache.HybridLevel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /**
* 功能描述:本地缓存
* 作者:唐泽齐
*/
@Component
public class DepthLocal implements HybridLevel {
/*自带读写锁*/
public volatile static Cache<String, Object> localCache = CacheBuilder.newBuilder().initialCapacity(144).expireAfterAccess(2, TimeUnit.SECONDS).build(); @Override
public Object cache(String key, String id, Object o, boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
String keyId = key + ":" + id;
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
if (search && o == null && (o = localCache.getIfPresent(keyId)) == null) {
synchronized (ms.getMethod()) {
if ((o = localCache.getIfPresent(keyId)) == null) {
o = joinPoint.proceed();
localCache.put(keyId, o);
}
}
}
if (o == null) {
o = localCache.getIfPresent(keyId);
} else {
localCache.put(keyId, o);
}
return o;
} @Override
public Object del(String key, boolean self, ProceedingJoinPoint joinPoint) throws Throwable {
Object o = null;
if (self) {
o = joinPoint.proceed();
}
localCache.cleanUp();
return o;
}
}

DepthLocal

package com.*.server.live.core.hybridCache.impl;

import cn.hutool.core.util.RandomUtil;
import com.*.common.redis.service.RedisService;
import com.*.server.live.core.hybridCache.HybridLevel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component; import javax.annotation.Resource; /**
* 功能描述:redis缓存
* 作者:唐泽齐
*/
@Component
public class DepthRedis implements HybridLevel { @Resource
private RedisService redisService;
@Resource
private HybridLevel depthLocal; @Override
public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
o = depthLocal.cache(key, id, o,false,joinPoint);
String lock = getLock(key, id);
if(search && o== null && (o=redisService.hget(key , id)) == null ) {
for (;;) {
if(redisService.incr(lock,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
try {
o = joinPoint.proceed();
redisService.hset(key , id, o, 10l);
} catch (Throwable e) {
throw e;
}finally {
redisService.del(lock);
}
} else {
o = redisService.hget(key , id);
}
if(o == null) {
Thread.sleep(1);
} else {
break;
}
}
}
if (o == null) {
o = redisService.hget(key , id);
} else {
depthLocal.cache(key, id, o,false,joinPoint);
redisService.hset(key , id, o, 10l);
}
return o;
} @Override
public Object del(String key,boolean self, ProceedingJoinPoint joinPoint) throws Throwable {
Object o = null;
if (self) {
o = joinPoint.proceed();
}
redisService.del(key);
depthLocal.del(key,false,joinPoint);
return o;
} private String getLock(String key, String id) {
return this.getClass().getSimpleName()+":"+key+":"+id;
} }

DepthRedis

package com.*.server.live.core.hybridCache.impl;

import com.*.server.live.core.hybridCache.HybridType;
import org.springframework.stereotype.Component; /**
* 功能描述:全局级别
* 作者:唐泽齐
*/
@Component
public class ScopeGlobal implements HybridType {
@Override
public String token() {
return "";
}
}

ScopeGlobal

package com.*.server.live.core.hybridCache.impl;

import com.*.common.core.utils.*Util;
import com.*.server.live.core.hybridCache.HybridType;
import org.springframework.stereotype.Component; /**
* 功能描述:token级别
* 作者:唐泽齐
*/
@Component
public class ScopeToken implements HybridType {
@Override
public String token() {
return *Util.getCurrentTokenValue();
}
}

ScopeToken

问题修复:
1.json问题: 类HybridCacheInterceptor调整使用JSON.toJSON(args).toString()

2.死循环问题:类DepthRedis调整使用

  

@Override
public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
o = depthLocal.cache(key, id, o,false,joinPoint);
final String lock = getLock(key, id);
if(search && o== null && (o=redisService.hget(key , id)) == null ) {
if(redisService.incr(lock,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
try {
o = joinPoint.proceed();
redisService.hset(key , id, o, RandomUtil.randomLong(10l,20l));
} catch (Throwable e) {
throw e;
}finally {
redisService.del(lock);
}
} else {
Thread.sleep(10);
o = redisService.hget(key , id);
}
}
if (o == null) {
Thread.sleep(10);
o = redisService.hget(key , id);
} else {
depthLocal.cache(key, id, o,false,joinPoint);
redisService.hset(key , id, o, 10l);
}
return o;
}

问题修复:
1.取消清除缓存时的常规重试机制:

HybridCacheInterceptor -->
public Object del(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
String updateKey = getCacheKey(method);
HybridChange cacheAnnotation = method.getAnnotation(HybridChange.class);
Method method1 = cacheAnnotation.clazz().getMethod(cacheAnnotation.method(),cacheAnnotation.args());
HybridCache cache = method1.getAnnotation(HybridCache.class);
String key = getCacheKey(method1);
try {
HybridLevel hybridLevel = applicationContext.getBean(cache.depth());
Object o = hybridLevel.del(key, true, joinPoint);
log.info("多级缓存 清除 {}-->{}",updateKey,key);
return o;
} catch (Exception e) {
log.info("多级缓存 清除失败 {}-->{}:{}",updateKey,key,e);
throw e;
}
}
问题修复:
1、分布式情况下缓存死锁问题:
@Override
public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
o = depthLocal.cache(key, id, o,false,joinPoint);
final String lock = getLock(key, id);
if(search && o== null && (o=redisService.hget(key , id)) == null ) {
if(redisService.increx(lock,1l,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
try {
o = joinPoint.proceed();
redisService.hset(key , id, o, RandomUtil.randomLong(10l,20l));
} catch (Throwable e) {
throw e;
}finally {
redisService.del(lock);
}
} else {
Thread.sleep(10);
o = redisService.hget(key , id);
}
}
if (o == null) {
Thread.sleep(10);
o = redisService.hget(key , id);
} else {
depthLocal.cache(key, id, o,false,joinPoint);
redisService.hset(key , id, o, 10l);
}
return o;
}

分布式多级缓存(本地缓存,redis缓存)的更多相关文章

  1. 分布式改造剧集之Redis缓存采坑记

    Redis缓存采坑记 ​ 前言 ​ 这个其实应该属于分布式改造剧集中的一集(第一集见前面博客:http://www.cnblogs.com/Kidezyq/p/8748961.html),本来按照顺序 ...

  2. 分布式中为什么要加入redis缓存的理解

    面我们介绍了mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存.那么什么是分布式缓存呢?假设现在有两个服务器1和2,用户访问的时候访问了1服务器,查询后的缓存就会放在1服务器 ...

  3. 缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)

    一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cooki ...

  4. 缓存工厂之Redis缓存

    这几天没有按照计划分享技术博文,主要是去医院了,这里一想到在医院经历的种种,我真的有话要说:医院里的医务人员曾经被吹捧为美丽+和蔼+可亲的天使,在经受5天左右相互接触后不得不让感慨:遇见的有些人员在挂 ...

  5. Laravel之路——file缓存修改为redis缓存

    1.Session: 修改.evn文件: SESSION_DRIVER:redis (如果还不行的话,修改config/session.php的driver) 2.缓存修改为redis 注意:使用 L ...

  6. 缓存策略:redis缓存之springCache

    最近通过同学,突然知道服务器的缓存有很多猫腻,这里通过网上查询其他人的资料,进行记录: 缓存策略 比较简单的缓存策略: 1.失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放 ...

  7. 本地缓存,Redis缓存,数据库DB查询(结合代码分析)

    问题背景 为什么要使用缓存?本地缓存/Redis缓存/数据库查询优先级? 一.为什么要使用缓存 原因:CPU的速度远远高于磁盘IO的速度问题:很多信息存在数据库当中的,每次查询数据库就是一次IO操作所 ...

  8. SpringBoot集成Redis分布式锁以及Redis缓存

    https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...

  9. Redis 缓存失效机制

    Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计 ...

  10. TP5中用redis缓存

    在config.php配置文件下找到缓存设置,将原来的文件缓存修改为redis缓存,也可以改为多种类型的缓存: // +---------------------------------------- ...

随机推荐

  1. 鲜花:bitset求解高维偏序

    书接上回 一维偏序直接做.二维偏序套线段树或归并排序.三维偏序可以树套树或者 CDQ 套树,那四维偏序呢?可以 CDQ 套树套树.那五维偏序呢?可以发现,无论是 CDQ 分治还是树,都很难再继续嵌套, ...

  2. ARC143D Bridges

    ARC143D Bridges 巧妙的图论题. 思路 分析题目,发现很像拆点. 由于拆点要设置出入点,这里我们也把 \(a_i\) 设成入点,把 \(a_i+n\) 设成出点,再次分析问题. 考虑我们 ...

  3. mysql8 安装后无法登录的问题

    使用 apt 安装mysql 先search一下 sudo apt search mysql 得到结果 找到了这个 发现是8 那就装吧 sudo apt-get install mysql-serve ...

  4. GAN和CGAN——生成式对抗网络和条件生成式对抗网络

    GAN的定义 GAN是一个评估和学习生成模型的框架.生成模型的目标是学习到输入样本的分布,用来生成样本.GAN和传统的生成模型不同,使用两个内置模型以"对抗"的方式来使学习分布不断 ...

  5. 2025年前端面试准备js篇

    1.js的基本数据类型有哪些 undefined,null,bo0lean,number,string,object,Symbol,bigInt 分为原始类型和引用类型 原始类型:undefined, ...

  6. Python:pygame游戏编程之旅七(pygame基础知识讲解1)

    与Python自带的random.math.time等模块一样,Pygame框架也带有许多模块来提供绘图.播放声音.处理鼠标输入等功能. 本章将讲述Pygame提供的基本模块及功能,并假设读者已经具有 ...

  7. Docker之基础(一)

    接触Docker有很久一段时间, 但是没有好好总结一下, 借此公司项目全面容器化, 记录一下常用的Docker操作 概况: 本次容器化的项目包括PHP+Python项目,PHP是基于php-fpm的基 ...

  8. 如何正确使用 RMQ

    序列分块.设块长为 \(B\).每块预处理出最大值.对于询问 \([l, r]\),答案就是整块最大值和散块最大值拼起来.答案显然是 \(O(n) \sim O(\dfrac{n}{B} + B)\) ...

  9. git 推送代码到多个 远端仓库

    业务场景 在开发代码时,有时希望将代码推送到两个远端仓库. 实现方法 git remote add origin giturl1 git remote add backup giturl2 git p ...

  10. Yacc笔记

    语义动是一个C语句的序列 $$ 表是和相应产生式头的非终结符号关联的属性值 $i  表示和相应产生式体中第 i 个文法符号(终结符或非终结符号)关联的属性值 按照产生式规约时会执行关联的语义动作 对于 ...