多租缓存实现方案 (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);
    }

}

 

posted @ 2021-03-10 06:51  猿语  阅读(195)  评论(0)    收藏  举报