多租缓存实现方案 (Java)
多租缓存实现方案 (Java)
缓存在系统中是不可少的,缓存的实现是一个从无到有的过程,最开始,单应用的,缓存都是应用内部的,Map基本就能满足,实现简单。但是当上了微服务之后,应用是多部署的,应用之间的缓存在用Map就无法共享了,如果在调用一次其它服务,此时开销就会增大。
Redis 目前可能是主流的,但是后续可能不用Redis ,可能用其它的内存缓存,如何让程序实现尽可能少的变化,同时支持通用的处理(如多租的缓存切换、增删、刷新等通用的实现),因此封装了通用缓存功能,缓存整体设计如下:

MuliTenantCache 顶层为缓存的基础对外接口(查询、刷新等)
public interface MuliTenantCache<KEY, OBJ> {
boolean repeatedLoadding();
boolean isReadonly();
OBJ getObj(KEY key);
List<OBJ> getObjs();
List<OBJ> getObjs(List<KEY> keys);
void refresh();
void refresh(List<KEY> keys);
void flush();
void flushKeys(List<KEY> keys);
void add(List<OBJ> values);
void add(KEY key, OBJ value);
}
AbstractMuliTenantCache 主要是个模板实现,实现MuliTenantCache对外的方法,根据存储或用户自己的配置组合缓存。
public abstract class AbstractMuliTenantCache<T, M> implements MuliTenantCache<T, M> {
protected MuliTenantCacheConfig<T, M> config;
protected MuliTenantCacheStorage<T, M> storage;
protected Class<M> clazzValue;
@Override
public final void add(List<M> values) {
readonly();
storage.doAdd(values);
}
@Override
public final void add(T key, M value) {
readonly();
storage.doAdd(key, value);
}
@Override
public boolean isReadonly() {
return false;
}
@Override
public boolean repeatedLoadding() {
return true;
}
@Override
public M getObj(T key) {
M m = storage.getCacheObj(key);
if (m == null && repeatedLoadding()) {
m = config.query(key);
add(key, m);
m = storage.getCacheObj(key);
}
return m;
}
@Override
public List<M> getObjs() {
return new ArrayList<>(storage.getCachesObjs());
}
@Override
public List<M> getObjs(List<T> keys) {
if (SbzObjectUtils.isNullOrEmpty(keys)) {
return Collections.EMPTY_LIST;
}
return storage.getCachesObjs(keys);
}
@Override
public final void refresh() {
flush();
add(config.query());
}
@Override
public final void refresh(List<T> keys) {
flushKeys(keys);
add(config.query(keys));
}
@Override
public final void flushKeys(List<T> keys) {
readonly();
storage.doFlushKeys(keys);
}
@Override
public final void flush() {
readonly();
storage.doFlush();
}
private void readonly() {
if (isReadonly()) {
throw new SystemException("springbreeze cache is readonly ,can't set or refresh");
}
}
protected T getKey(T key, M m) {
if (key != null) {
return key;
}
T result = config.getKey(m);
return result;
}
protected Class<M> getClazzValue() {
return this.clazzValue;
}
protected void setConfig(MuliTenantCacheConfig<T, M> config) {
this.config = config;
}
}
MuliTenantCacheConfig主要是缓存维护的逻辑,如数据源、配置是否只读、缓存分类等。
public interface MuliTenantCacheConfig<KEY, OBJ> {
boolean repeatedLoadding();
boolean isReadonly();
String cacheType();
KEY getKey(OBJ m);
List<OBJ> query();
OBJ query(KEY t);
List<OBJ> query(List<KEY> ts);
}
MuliTenantCacheStorage 主要给缓存应用做的扩展,如Redis缓存和Map缓存需要分别实现对应点逻辑。
public interface MuliTenantCacheStorage<KEY, OBJ> {
OBJ getCacheObj(KEY key);
List<OBJ> getCachesObjs(List<KEY> key);
List<OBJ> getCachesObjs();
OBJ doAdd(KEY key, OBJ m);
List<OBJ> doAdd(List<OBJ> values);
void doFlushKeys(List<KEY> keys);
void doFlush();
}
AbstractMuliTenantCacheBase 实现和组合 AbstractMuliTenantCache 逻辑,创建工作在 builder 中进行。
public abstract class AbstractMuliTenantCacheBase<KEY, OBJ> extends AbstractMuliTenantCache<KEY, OBJ>
implements MuliTenantCacheConfig<KEY, OBJ> { @Autowired
private SbzMuliTenantCacheBuilder<KEY, OBJ> builder; @PostConstruct
public void init() {
this.config = this;
this.storage = (MuliTenantCacheStorage<KEY, OBJ>) builder.build(this, getGenericClass());
} @SuppressWarnings("unchecked")
private Class<OBJ> getGenericClass() {
return (Class<OBJ>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
}
}
SbzMuliTenantCacheBuilder 复制对象的创建,后续增加配置创建Map 或 其他的缓存存储。
@Component
public class SbzMuliTenantCacheBuilder<KEY, OBJ> { @Autowired
protected RedisService redisService; public AbstractMuliTenantCache<KEY, OBJ> build(MuliTenantCacheConfig<KEY, OBJ> config, Class<OBJ> clazz) {
RedisAbstractMuliTentantCache<KEY,
OBJ> cache = new RedisAbstractMuliTentantCache<>(config, redisService, clazz); return cache;
}
}
RedisMuliTentantCache 为Redis 的实现方式。
public class RedisMuliTentantCache<T, M> extends AbstractMuliTenantCache<T, M>
implements MuliTenantCacheStorage<T, M> { protected RedisService redisService; public RedisAbstractMuliTentantCache(MuliTenantCacheConfig<T, M> config, RedisService redisService,
Class<M> clazzM) {
this.redisService = redisService;
this.storage = this;
this.clazzValue = clazzM;
this.config = config;
} protected String getRedisKey(T key) {
return MessageFormat.format("{0}:{1}:{2}", UserInfo.getCurrentOrgCode(), config.cacheType(), key);
} protected String getRedisStrKey(String key) {
return MessageFormat.format("{0}:{1}:{2}", UserInfo.getCurrentOrgCode(), config.cacheType(), key);
} @Override
public M doAdd(T key, M m) {
redisService.set(getRedisKey(getKey(key, m)), JSON.toJSONString(m));
return m;
} @Override
public List<M> doAdd(List<M> values) {
if (!SbzObjectUtils.isNullOrEmpty(values)) {
values.forEach(s -> {
doAdd(config.getKey(s), s);
});
}
return values;
} @Override
public void doFlushKeys(List<T> keys) {
if (!SbzObjectUtils.isNullOrEmpty(keys)) {
String[] redisKeys = keys.stream().map(t -> getRedisKey(t)).toArray(String[]::new);
redisService.remove(redisKeys);
}
} @Override
public void doFlush() {
redisService.removePattern(getRedisStrKey("*"));
} @Override
public M getCacheObj(T key) {
String redisValue = redisService.getByKeyGenerial(getRedisKey(key));
if (!SbzStringUtils.isNullOrEmpty(redisValue)) {
M result = toObj(redisValue, getClazzValue());
return result;
} return null;
} @Override
public List<M> getCachesObjs() {
List<String> redisValues = redisService.getByPatternGenerial(getRedisStrKey("*"));
if (!SbzObjectUtils.isNullOrEmpty(redisValues)) {
List<M> result = new ArrayList<>(redisValues.size());
redisValues.forEach(s -> result.add(toObj(s, getClazzValue())));
return result;
} return Collections.EMPTY_LIST;
} @Override
public List<M> getCachesObjs(List<T> keys) {
Set<Serializable> redisKeys = new HashSet<>();
keys.forEach(s -> {
redisKeys.add(getRedisKey(s));
});
List<String> redisValues = redisService.getByKeysGenerial(redisKeys);
if (!SbzObjectUtils.isNullOrEmpty(redisValues)) {
List<M> result = new ArrayList<>(redisValues.size());
redisValues.forEach(s -> result.add(toObj(s, getClazzValue())));
return result;
} return Collections.EMPTY_LIST;
} public static <T> T toObj(String json, Class<T> t) {
return JSON.parseObject(json, t);
}
调用过程很简单。
@Component
public class DemoCache extends SbzAbstractMuliTenantCacheBase<String, DemoCacheInfo> { @Override
public String cacheType() {
return "DEMO_CACHE";
} @Override
public String getKey(DemoCacheInfo m) {
return m.getKey();
} @Override
public List<DemoCacheInfo> query() {
return DemoCacheInfo.get(Arrays.asList("C1", "C2", "C3"));
} @Override
public DemoCacheInfo query(String t) {
return LambdaUtils.firstOrDefault(DemoCacheInfo.get(Arrays.asList("C1")));
} @Override
public List<DemoCacheInfo> query(List<String> ts) {
return DemoCacheInfo.get(ts);
} }
多租缓存实现方案 (Java)的更多相关文章
- atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener
atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs ...
- Web缓存(Varnish方案)
Web缓存(Varnish方案) 转载 http://www.s135.com/post/313/ arnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang (htt ...
- shiro使用redis作为缓存,出现要清除缓存时报错 java.lang.Exception: Failed to deserialize at org.crazycake.shiro.SerializeUtils.deserialize(SerializeUtils.java:41) ~[shiro-redis-2.4.2.1-RELEASE.jar:na]
shiro使用redis作为缓存,出现要清除缓存时报错 java.lang.Exception: Failed to deserialize at org.crazycake.shiro.Serial ...
- Atitit.软件命名空间 包的命名统计 及命名表(2000个名称) 方案java package
Atitit.软件命名空间 包的命名统计 及命名表(2000个名称) 方案java package 1. 统计的lib jar 列表1 2. Code3 3. 常用包名按找字母排序(2000个)4 ...
- iOS 本地缓存实现 方案借鉴
在手机应用程序开发中,为了减少与服务端的交互次数,加快用户的响应速度,一般都会在iOS设备中加一个缓存的机制,前面一篇文章介绍了iOS设备的内存缓存,这篇文章将设计一个本地缓存的机制. 功能需求 这个 ...
- Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX. 出现这种异常,我需要自 ...
- Window Redis分布式部署方案 java
Redis分布式部署方案 Window 1. 基本介绍 首先redis官方是没有提供window下的版本, 是window配合发布的.因现阶段项目需求,所以研究部署的是window版本的,其实都 ...
- 如何利用缓存机制实现JAVA类反射性能提升30倍
一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> ...
- 第三章 - CPU缓存结构和java内存模型
CPU 缓存结构原理 CPU 缓存结构 查看 cpu 缓存 速度比较 查看 cpu 缓存行 cpu 拿到的内存地址格式是这样的 CPU 缓存读 根据低位,计算在缓存中的索引 判断是否有效 0 去内存读 ...
随机推荐
- CF1462-E2. Close Tuples (hard version)
本题为hard版,还有一个easy版,区别在于k和m的取值不同. 题意: 给出一个由n个数字组成的数组 \(a\).现在定义一种子集为\(\{A_1, A_2, A_3, ..., A_m\}\),使 ...
- Nginx基础 - 配置缓存web服务
1.缓存配置语法 1)proxy_cache配置语法 Syntax: proxy_cache zone | off; Default: proxy_cache off; Context: http, ...
- Ubuntu第一次使用注意点
第一次装完Ubuntu登录,打开命令行,登录的不是root权限,切换root不成功: 这个问题产生的原因是由于Ubuntu系统默认是没有激活root用户的,需要我们手工进行操作,在命令行界面下,或者在 ...
- 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)
什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...
- Linux 学习笔记分享: Linux 用户组的权限: drwx------ 700权限(d 目录 ,r=4,w=2,x=1:rwx=7;---=0;---=0)
Linux 用户组的权限: drwx------ 700权限(d 目录 ,r=4,w=2,x=1:rwx=7;---=0;---=0) 1 1 Linux 用户组的权限: drwx------ 700 ...
- pure CSS3 实现三角形icon的方法
pure CSS3 实现三角形icon的方法 border: color+transparent transform : rotate() /rotateZ() ? 使用 实体字符"◆&qu ...
- styled-components all in one
styled-components all in one CSS in JS https://www.styled-components.com/ https://github.com/styled- ...
- git & github & git clone & 'git clone' failed with status 128
git & github & git clone & 'git clone' failed with status 128 'git clone' failed with st ...
- Taro 版本
Taro 版本 https://taro-docs.jd.com/taro/versions.html 1.x 1.3.34 https://taro-docs.jd.com/taro/docs/1. ...
- github & code owners
github & code owners https://help.github.com/en/github/creating-cloning-and-archiving-repositori ...