jetcache 2级缓存模式实现批量清除

news/2024/4/27 9:03:52

需求

希望能够实现清理指定对象缓存的方法,例如缓存了User表,当User表巨大时,通过id全量去清理不现实,耗费资源也巨大。因此需要能够支持清理指定本地和远程缓存的批量方法。

分析

查看jetcache生成的cache接口,并没有提供一个例如getAll()或invalidate的方法。因此需要能够扩展;

查看jetcache源码,jetcache对于单级和多级缓存实现了统一的cache接口;

当为单级缓存时,直接返回缓存的包装Cache;

当为多级缓存时,返回MultiLevelCache;

所有的缓存,都实现了接口的unwrap方法,当指定一个类型时,会返回对应的包装Cache,否则抛出IllegalArgumentException;

源码里MultiLevelCache的unwrap如下:

    @Overridepublic <T> T unwrap(Class<T> clazz) {Objects.requireNonNull(clazz);for (Cache cache : caches) {try {T obj = (T) cache.unwrap(clazz);if (obj != null) {return obj;}} catch (IllegalArgumentException e) {// ignore}}throw new IllegalArgumentException(clazz.getName());}

也就是说,当多级缓存(这里是2级,实际可以支持多级)时,可以通过类型指定unwarp到对应的缓存,这样我们就可以通过cache拿到对应的本地缓存,进而调用缓存全量清理方法

实现

因此,实现思路如下:

对于本地的缓存,直接unwrap后全量清理掉;

对于远程的缓存,直接redis缓存访问,并通过key的查询方法,将符合条件的缓存批量清理掉;

对于其他机器的缓存,采用订阅发布的方式,让其他机器收到消息后unwrap并清理本地缓存。

1) 本地缓存处理

将jetcache的缓存拿出来unwrap即可调用caffeine cache的invalidateAll实现清理本地缓存,实现一个方法:

	@Overridepublic void evictCacheLocal() {//清理本地if(cache != null){com.github.benmanes.caffeine.cache.Cache caffeineCache = cache.unwrap(com.github.benmanes.caffeine.cache.Cache.class);caffeineCache.invalidateAll();}}

2) 远程缓存处理

远程的目前系统使用了redission驱动,使用RKeys将符合前缀的远程清理掉。getKeysByPattern是按10个一组SCAN得到,因此不会阻塞redis,按100个一组清理即可,如果数据较多,可以考虑更大点

	@Overridepublic void evictCacheAll() {// 清理redisRKeys keys = redissonClient.getKeys();Iterator<String> keysList = keys.getKeysByPattern(GlobalEx.CACHEREGION_ENTITY + entityClass.getSimpleName() + "-*").iterator();List<String> processList = new ArrayList<>();while(keysList.hasNext()) {processList.add(keysList.next());if(processList.size() == 100){keys.delete(processList.toArray(new String[processList.size()]));processList.clear();}}if(processList.size() > 0){keys.delete(processList.toArray(new String[processList.size()]));}}

3) 其它机器缓存处理

研究了下源码,jetcache没有很方便的扩展点,因此直接绕过jetcache的订阅发布。直接用其它组件去实现订阅发布,目前系统已经引入了redission,因此直接使用redission的订阅/发布。

这里实现了一个订阅监听列表,直接在subsys.<子系统>.notify添加自己的发布/监听器即可

监听消息类,notifyType可以区别消息类型

package org.ccframe.commons.notify;import com.alibaba.fastjson2.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class NotifyMessage {private int notifyType;@JSONField(name = "message")private String message;
}

消息监听基类,使用Json通知消息

package org.ccframe.commons.notify;import com.alibaba.fastjson2.JSON;
import lombok.extern.log4j.Log4j2;
import org.ccframe.commons.helper.CcNotifyHelper;
import org.springframework.beans.factory.annotation.Autowired;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;@Log4j2
public abstract class BaseNotifyListener<T> {public abstract int getNotifyType();private final Class<T> messageClass;@Autowiredprivate CcNotifyHelper ccNotifyHelper;public BaseNotifyListener(){Type genType = getClass().getGenericSuperclass();this.messageClass = (Class<T>) ((ParameterizedType) genType).getActualTypeArguments()[0];}public void receiveData(String notifyMessage){T message = JSON.parseObject(notifyMessage, messageClass);try {process(message);}catch (Throwable tr){log.error(tr);}}public void sendData(T notifyMessage){ccNotifyHelper.sendNotify(getNotifyType(), JSON.toJSONString(notifyMessage));}protected abstract void process(T message);
}

记得配置一下包扫描

@ComponentScan({"org.ccframe.subsys.*.notify" //所有的订阅广播
})

然后写一个子类,收到消息后调用baseservice的清理方法清理本地缓存

package org.ccframe.subsys.core.notify;import org.apache.commons.lang3.StringUtils;
import org.ccframe.commons.base.BaseService;
import org.ccframe.commons.helper.SpringContextHelper;
import org.ccframe.commons.notify.BaseNotifyListener;
import org.springframework.stereotype.Component;@Component
public class ClearLocalCacheNotifyListener extends BaseNotifyListener<String> {@Overridepublic int getNotifyType() {return 0;}@Overrideprotected void process(String message) {SpringContextHelper.getBean(StringUtils.uncapitalize(message) + "Service", BaseService.class).evictCacheLocal();}
}

因此在evictCacheAll最后调用发送清理消息,收到所有的订阅清空本地对应对象缓存即可

	@Overridepublic void evictCacheAll() {// 清理redisRKeys keys = redissonClient.getKeys();Iterator<String> keysList = keys.getKeysByPattern(GlobalEx.CACHEREGION_ENTITY + entityClass.getSimpleName() + "-*").iterator();List<String> processList = new ArrayList<>();while(keysList.hasNext()) {processList.add(keysList.next());if(processList.size() == 100){keys.delete(processList.toArray(new String[processList.size()]));processList.clear();}}if(processList.size() > 0){keys.delete(processList.toArray(new String[processList.size()]));}// 广播清理本地缓存clearLocalCacheNotifyListener.sendData(entityClass.getSimpleName());}

写个controller方法测试一下

    @DubboReference(check=false)private IUserService userService;@GetMapping("test")public QuartzRowDto test(@ApiIgnore HttpServletRequest request) {userService.evictCacheAll();return new QuartzRowDto();}

检查一下,能够收到清理消息清理本地缓存

这样,即可快速的完成本地和远程的指定表缓存的清理,也不受表数据过大影响了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/11155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

JVM垃圾回收机制

文章目录 1.标记清除法&#xff08;标记不能清除的&#xff0c;清除其余的&#xff09;2.标记整理法3.标记复制法(标记不能清除的)分代回收思想&#xff1a;三色标记法漏标问题垃圾回收规模&#xff1a;什么是STW&#xff1f;并发标记常见的垃圾回收器的实现Parallel GCConcurr…

151 shell编程,正则表达式,在C语言中如何使用正则表达式

零&#xff0c;坑点记录&#xff1a;bash 和 dash 的区别&#xff0c;导致的坑点 查看当前用的shell 是啥&#xff0c;用的是/bin/bash hunandedehunandede-virtual-machine:~$ echo $SHELL /bin/bash 当shell 脚本运行的时候&#xff08;后面会学到方法&#xff0c;这里是最…

耳目一新的滑块版登录注册界面~

又到了毕业季&#xff0c;大家做毕设的时候总会参考已有的案例&#xff0c;不过大多产品的样式非常单一雷同。本帖博主给大家分享一个比较别树一帜的登录界面&#xff0c;如下&#xff1a; 如果没有账号&#xff0c;点击“去注册”&#xff0c;则会产生如下的效果&#xff1a; …

倍压器电路原理及仿真

倍压器是利用二极管单向导通的特性和电容两端电压不能突变且可以存储能量的特性&#xff0c;使得能量逐步往后级输送&#xff0c;同时线路上的电压也逐渐升高。因此&#xff0c;它可以实现将较低的交流电压转换成一个较高的直流电压。根据倍压的原理&#xff0c;有二倍压、三倍…

绝地求生:PUBG七周年:荣都人机进阶打法!

再给大家写一点攻略&#xff0c;荣都又上线了&#xff0c;但是我发现很多朋友打个人机都不利索。 原因在于荣都的两个人机点NPC太多&#xff0c;并且还是三级兑换点跳人很多&#xff0c;一旦打起来情况非常混乱&#xff0c;最近和朋友总结一套人机打法。 食王府 食王府一定要从…

AQS源码分析

前言 AbstractQueuedSynchronizer是抽象同步队列&#xff0c;其是实现同步机器的基础组件&#xff0c;并发包中的锁的底层就是使用AQS实现的。AQS中 维护了一个volatile int state&#xff08;代表共享资源&#xff09;和一个FIFO线程等待队列&#xff08;多线程争用资源被阻塞…