多租缓存实现方案 (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 去内存读 ...
随机推荐
- Dapr微服务应用开发系列2:Hello World与SDK初接触
题记:上篇介绍了Dapr的环境配置,这次我们来动手尝试一下Dapr应用的开发 Hello World Dapr应用的Hello World其实和其他的Hello World一样简单: 首先用你喜欢的语 ...
- linux运维面试精华题
Linux运维跳槽面试精华题|第一集 1.什么是运维?什么是游戏运维? 1)运维是指大型组织已经建立好的网络软硬件的维护,就是要保证业务的上线与运作的正常,在他运转的过程中,对他进行维护,他集合了网络 ...
- Redis 事务 & 消息队列
Redis 消息队列介绍 什么是消息队列 消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠传递,消息生产者只管把消息发布到消息队列中而不 ...
- 关于TCP状态TIME_WAIT的理解
1.TIME_WAIT的作用: TIME_WAIT状态存在的理由:1)可靠地实现TCP全双工连接的终止 在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器 ...
- OPENSOURCE - libcurl
本文仅做备份存档,原文地址如下,请点链接进入 https://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html https://www.c ...
- mark::开源绘图工具graphviz
http://blog.csdn.net/iamljj/article/details/5862930 http://codeforces.com/contest/601/problem/D
- POJ 2288 Islands and Bridges(状压DP)题解
题意:n个点,m有向边,w[i]表示i的价值,求价值最大的哈密顿图(只经过所有点一次).价值为:所有点的w之和,加上,每条边的价值 = w[i] * w[j],加上,如果连续的三个点相互连接的价值 = ...
- macOS open url from terminal
macOS open url from terminal open URL && start terminal bash open url in chrome open chrome ...
- py 使用win32 api
http://timgolden.me.uk/pywin32-docs/contents.html https://docs.python.org/3/library/ctypes.html#ctyp ...
- Flutter: MobX和flutter_mobx状态管理器
MobX.dart网站上的 " 入门指南" mobxjs video 组织Stores 安装依赖 dependencies: mobx: flutter_mobx: dev_dep ...