多租缓存实现方案 (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); } }
never give up on your dreams !

浙公网安备 33010602011771号