分布式多级缓存(本地缓存,redis缓存)
结构包:
使用案例:
实现效果:
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缓存)的更多相关文章
- 分布式改造剧集之Redis缓存采坑记
Redis缓存采坑记 前言 这个其实应该属于分布式改造剧集中的一集(第一集见前面博客:http://www.cnblogs.com/Kidezyq/p/8748961.html),本来按照顺序 ...
- 分布式中为什么要加入redis缓存的理解
面我们介绍了mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存.那么什么是分布式缓存呢?假设现在有两个服务器1和2,用户访问的时候访问了1服务器,查询后的缓存就会放在1服务器 ...
- 缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)
一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cooki ...
- 缓存工厂之Redis缓存
这几天没有按照计划分享技术博文,主要是去医院了,这里一想到在医院经历的种种,我真的有话要说:医院里的医务人员曾经被吹捧为美丽+和蔼+可亲的天使,在经受5天左右相互接触后不得不让感慨:遇见的有些人员在挂 ...
- Laravel之路——file缓存修改为redis缓存
1.Session: 修改.evn文件: SESSION_DRIVER:redis (如果还不行的话,修改config/session.php的driver) 2.缓存修改为redis 注意:使用 L ...
- 缓存策略:redis缓存之springCache
最近通过同学,突然知道服务器的缓存有很多猫腻,这里通过网上查询其他人的资料,进行记录: 缓存策略 比较简单的缓存策略: 1.失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放 ...
- 本地缓存,Redis缓存,数据库DB查询(结合代码分析)
问题背景 为什么要使用缓存?本地缓存/Redis缓存/数据库查询优先级? 一.为什么要使用缓存 原因:CPU的速度远远高于磁盘IO的速度问题:很多信息存在数据库当中的,每次查询数据库就是一次IO操作所 ...
- SpringBoot集成Redis分布式锁以及Redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...
- Redis 缓存失效机制
Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计 ...
- TP5中用redis缓存
在config.php配置文件下找到缓存设置,将原来的文件缓存修改为redis缓存,也可以改为多种类型的缓存: // +---------------------------------------- ...
随机推荐
- K8s之运行时containerd安装和使用
一.containerd 1. 前生今世 很久以前,Docker 强势崛起,以"镜像"这个大招席卷全球,对其他容器技术进行致命的降维打击,使其毫无招架之力,就连 Google 也不 ...
- ABP发布后通过外部URL调用不到方法
MVC要与Host项目发布保持同步,如果Host工程不保持同步就会导致调用不到新的方法,因为找不得新的dll.
- SqlSugarClient 代码优先建表, 根据给定的实体类,创建SQL语句, 之后创建MySQL表
using SqlSugar; using System; using System.Collections.Generic; using System.Reflection; using Syste ...
- 洛谷P4913【深基16.例3】二叉树深度题解
很简单的二叉树遍历问题,可以用dfs(深度优先搜索)解决. 看到数据范围,最大不超过 \(10^6\) ,可以不开 long long (但我还是习惯性地开了) 接下来上代码: #include< ...
- 啃啃老菜:Spring IOC核心源码学习(一)
啃啃老菜:Spring IOC核心源码学习(一) 本文主要以spring ioc容器基本代码骨架为切入点,理解ioc容器的基本代码组件结构,各代码组件细节剖析将放在后面的学习文章里. 关于IOC容器 ...
- weex跨页面通信
需求: A页面有表单和表格,点击表格中的按钮到B页面,B页面操作完毕,再次回到A页面,表单元素保持不变,表格内容刷新. 通过管道通信去做,用两个管道嵌套,A页面跳转到B页面的时候,直接用管道发过去,B ...
- 德承工控机DX-1200 成功适配2024年6月6日发布的国产开源系统OpenEuler 24.03 LTS
基础软件双子星:欧拉系统(OpenEuler)& 鸿蒙系统(OpenHarmony),鸿蒙系统常应用在华为的手机和平板电脑上,大众也较为熟悉,是面向消费电子产品领域的系统:而欧拉系统则是面向服 ...
- winform窗体无边框拖动
1:引用命名空间 using System.Runtime.InteropServices; 2:想要拖动窗体的控件绑定MouseDown事件 点击查看代码 //窗体移动 [DllImport(&qu ...
- DDCA —— 片上网络互联
1. 路由 1.1 网络拓扑示例 Grid(网格) 网络拓扑通常是一个二维矩阵形式,每个节点(处理器)与其上下左右相邻的节点相连. 如果节点在边缘,某些方向上可能没有相邻节点(边界节点). Torus ...
- 10C++选择结构(4)——教学
一.switch语句 (第25课 成绩等级) 问题:风之巅小学规定,若测试成绩大于或等于90分为"A",大于或等于70分小于90分为"B",大于或等于60分小于 ...