统一缓存查询框架

发表信息: by

统一缓存查询框架

使用代码

    @Override
    public List<GoodsActivityGroupDTO> cacheQueryGoodsActivityGroups(Long goodsId, List<Long> activityGroupIds) {
        return batchQuery(BatCacheConstants.CACHE_PREFIX_GOODS_ACTIVITY_GROUP, // 标记
                activityGroupIds, CacheKeyGenerator::genGoodsActivityGroupCacheKey, // cache key 生成规则
                CacheValueSerialize::serializeGoodsActivityGroup, CacheValueSerialize::deSerializeGoodsActivityGroup // 序列化反序列方式
                , missIds -> { // miss 之后 查询数据的逻辑
                    List<GoodsActivityGroupDTO> groups = goodsActivityDBV2.batchQueryGoodsActivityGroup(goodsId, missIds);
                    return Optional.ofNullable(groups).orElse(new ArrayList<>()).stream().collect( Collectors.toMap(GoodsActivityGroupDTO::getActivityGroupId, Function.identity(), (v1, v2) -> v2));
                });
    }

实现代码


public class CacheQuery {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private BatTemplate newBatTemplate;

    /**
     * 批量查询数据(包装了 从缓存查询,miss之后从DB查询等逻辑)
     *
     * @param tag             标记(用作打点)
     * @param idList          实体ID列表
     * @param keyGenerator    缓存key 生成器
     * @param serialize       序列化方式
     * @param deSerialize     反序列化方式
     * @param missGetFunction miss 之后的操作逻辑
     */
    <T, M> List<T> batchQuery(String tag,
            List<M> idList, Function<M, String> keyGenerator,
            Function<T, String> serialize, Function<String, T> deSerialize,
            Function<List<M>, Map<M, T>> missGetFunction) {
        List<T> result = new ArrayList<>();

        // 没有id,直接返回
        if (CollectionUtils.isEmpty(idList)) {
            return result;
        }

        // 映射id成缓存的: key: id
        Map<String, M> keyMapId = idList.stream()
                .collect(Collectors.toMap(keyGenerator, Function.identity(), (v1, v2) -> v2));

        // 查缓存
        int size = keyMapId.size();
        String[] cacheKeys = keyMapId.keySet().toArray(new String[size]);
        List<KeyValue<String, String>> keyValueList = newBatTemplate.mget(cacheKeys);

        List<M> hitCacheIds = new ArrayList<>(); // 记录命中缓存的id
        List<M> missCacheIds = new ArrayList<>(); // 记录未命中缓存的id

        // 将缓存中存在的数据 反序列化成对象,存到结果里面
        // 将缓存中不存在的数据 整理成 missCacheId
        for (KeyValue<String, String> keyValue : keyValueList) {
            String cacheKey = keyValue.getKey();
            M groupId = keyMapId.get(cacheKey);

            if (!keyValue.hasValue()) {
                missCacheIds.add(groupId);
            } else {
                hitCacheIds.add(groupId);
                T cached = deSerialize.apply(keyValue.getValue());
                result.add(cached);
            }
        }

        // 如果存在 没有缓存的id,查询 missGetFunction ,之后再把查询到的结果缓存起来
        if (CollectionUtils.isNotEmpty(missCacheIds)) {
            // 查"DB"
            Map<M, T> dbValues = missGetFunction.apply(missCacheIds);
            if (MapUtils.isNotEmpty(dbValues)) { // DB查到结果且不为null,将结果存到返回值中
                dbValues.forEach((k, v) -> {
                    if (null != v) {
                        result.add(v);
                    }
                });
            }

            if (MapUtils.isNotEmpty(dbValues)) { // DB查到结果且不为null,将结果存到Cache中
                Map<String, String> kv = new HashMap<>();
                for (Entry<M, T> entry : dbValues.entrySet()) {
                    if (null == entry.getValue()) {
                        continue;
                    }

                    kv.put(keyGenerator.apply(entry.getKey()), serialize.apply(entry.getValue()));
                }
                Boolean success = newBatTemplate.execute(action -> {
                    BatPipeline pipelined = action.pipelined();
                    kv.forEach((k, v) -> pipelined.set(k, v, Builder.ex(BatCacheConstants.COMMON_EXPIRE_TIME)));
                    return pipelined.sync();
                });

                if (!success) {
                    logger.error("Bat exec pipelined error", new LibraBatException("bat pipeline exec error"));
                }
            }
        }

        // 打点
        String type = String.format("Cache-%s", tag);
        if (hitCacheIds.size() > 0) {
            Cat.logEvent(type, "hit", Transaction.SUCCESS, null, hitCacheIds.size());
        }
        if (missCacheIds.size() > 0) {
            Cat.logEvent(type, "miss", Transaction.SUCCESS, null, missCacheIds.size());
        }

        return result;
    }

    <T, M> T query(String tag,
            M id, Function<M, String> keyGenerator,
            Function<T, String> serialize, Function<String, T> deSerialize,
            Function<M, T> missGetFunction) {
        // 映射id成缓存的: key: id
        String cacheKey = keyGenerator.apply(id);

        // 打点
        String type = String.format("Cache-%s", tag);

        // 查缓存
        String cacheValue = newBatTemplate.get(cacheKey);

        // 将缓存中存在的数据 反序列化成对象,直接返回
        if (StringUtils.isNotBlank(cacheValue)) {
            Cat.logEvent(type, "hit", Transaction.SUCCESS, null, 1);
            return deSerialize.apply(cacheValue);
        }

        // 如果存在 没有缓存的id,查询 missGetFunction ,之后再把查询到的结果缓存起来
        T dbValue = missGetFunction.apply(id);
        Cat.logEvent(type, "miss", Transaction.SUCCESS, null, 1);

        if (null != dbValue) {
            newBatTemplate.set(cacheKey, serialize.apply(dbValue), Builder.ex(BatCacheConstants.COMMON_EXPIRE_TIME));
        }

        return null;
    }

    <M> void clear(String tag, M id, Function<M, String> keyGenerator) {
        // 映射id成缓存的: key: id
        String cacheKey = keyGenerator.apply(id);

        newBatTemplate.expire(cacheKey, 1);

        // 打点
        String type = String.format("Cache-%s", tag);
        Cat.logEvent(type, "delete", Transaction.SUCCESS, null, 1);
    }
}