多租缓存实现方案 (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)的更多相关文章

  1. atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty  HttpListener

    atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty  HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs   ...

  2. Web缓存(Varnish方案)

    Web缓存(Varnish方案) 转载 http://www.s135.com/post/313/ arnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang (htt ...

  3. 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 ...

  4. Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package

    Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package 1. 统计的lib jar 列表1 2. Code3 3. 常用包名按找字母排序(2000个)4 ...

  5. iOS 本地缓存实现 方案借鉴

    在手机应用程序开发中,为了减少与服务端的交互次数,加快用户的响应速度,一般都会在iOS设备中加一个缓存的机制,前面一篇文章介绍了iOS设备的内存缓存,这篇文章将设计一个本地缓存的机制. 功能需求 这个 ...

  6. Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX

    Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX. 出现这种异常,我需要自 ...

  7. Window Redis分布式部署方案 java

    Redis分布式部署方案 Window 1.    基本介绍 首先redis官方是没有提供window下的版本, 是window配合发布的.因现阶段项目需求,所以研究部署的是window版本的,其实都 ...

  8. 如何利用缓存机制实现JAVA类反射性能提升30倍

    一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> ...

  9. 第三章 - CPU缓存结构和java内存模型

    CPU 缓存结构原理 CPU 缓存结构 查看 cpu 缓存 速度比较 查看 cpu 缓存行 cpu 拿到的内存地址格式是这样的 CPU 缓存读 根据低位,计算在缓存中的索引 判断是否有效 0 去内存读 ...

随机推荐

  1. CF1462-E2. Close Tuples (hard version)

    本题为hard版,还有一个easy版,区别在于k和m的取值不同. 题意: 给出一个由n个数字组成的数组 \(a\).现在定义一种子集为\(\{A_1, A_2, A_3, ..., A_m\}\),使 ...

  2. Nginx基础 - 配置缓存web服务

    1.缓存配置语法 1)proxy_cache配置语法 Syntax: proxy_cache zone | off; Default: proxy_cache off; Context: http, ...

  3. Ubuntu第一次使用注意点

    第一次装完Ubuntu登录,打开命令行,登录的不是root权限,切换root不成功: 这个问题产生的原因是由于Ubuntu系统默认是没有激活root用户的,需要我们手工进行操作,在命令行界面下,或者在 ...

  4. 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)

    什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...

  5. 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 ...

  6. pure CSS3 实现三角形icon的方法

    pure CSS3 实现三角形icon的方法 border: color+transparent transform : rotate() /rotateZ() ? 使用 实体字符"◆&qu ...

  7. styled-components all in one

    styled-components all in one CSS in JS https://www.styled-components.com/ https://github.com/styled- ...

  8. 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 ...

  9. Taro 版本

    Taro 版本 https://taro-docs.jd.com/taro/versions.html 1.x 1.3.34 https://taro-docs.jd.com/taro/docs/1. ...

  10. github & code owners

    github & code owners https://help.github.com/en/github/creating-cloning-and-archiving-repositori ...