多租缓存实现方案 (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 去内存读 ...
随机推荐
- MySQL 主从复制(下)
延时复制 因为延时复制主从数据同一时间不一致, 所以延时从库一般只能做备份,不提供任何对外服务 配置延时复制(已经有主从) 1.停止主从 mysql> stop slave; Query OK, ...
- LINUX - 寄存器和堆栈
堆栈模型: 函数调用: EBP:ESP EBP当前调用函数的栈底: ESP当前调用函数的栈顶: ---------------------------------------------------- ...
- codefoeces 864B
B. Polycarp and Letters time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- Linux内核实现透视---硬中断
Linux的中断处理是驱动中比较重要的一部分内容,要清楚具体的实现才能更好的理解而不是靠记住别人理解后总结的规律,所以今天就打算从从源码来学习一下Linux内核对于中断处理过程,设计中断子系统的初始化 ...
- Java开发工程师最新面试题库系列——集合部分(附答案)
集合 如果你有更好的想法请在评论区留下您的答案,一起交流讨论 说说常见的集合有哪些? 答:主要分List.Set.Map.Queue四类,其中包含ArrayList.LinkedList.HashSe ...
- Android 开启 WebView 页面 Chrome debug
Android 开启 WebView 页面 Chrome debug WebView debug // 开启 WebView 页面 debug testWebView.setWebContentsDe ...
- React Big Changes All in One
React Big Changes All in One React 重大更新 React Versions React 版本变更 https://reactjs.org/versions/ sema ...
- PHP & LAMP & WAMP
PHP & LAMP & WAMP https://github.com/xgqfrms/DataStructure/issues/7#issuecomment-430538438 h ...
- Versatile Python 3.x
Versatile Python 3.x TryPython Python 3.8.0 (default, Nov 14 2019, 22:29:45) [GCC 5.4.0 20160609] on ...
- Flutter & Scaffold & multiple floatingActionButton
Flutter & Scaffold & multiple floatingActionButton demo import 'package:flutter/material.dar ...