带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(1)https://developer.aliyun.com/article/1338386?groupCode=taobaotech
下面来看看我是如何优化的。核心主要关注:区分下游返回的结果是真的空还是假的空,本身就为空的情况下,就该缓存空集合(非大促期间或者某些榜没有数据,数据本身就为空)
在redis中拉长value缓存的时间,同时新增一个可更新时间的缓存(比如60s过期),当判断更新时间缓存过期了,就重新读取数据源,将value值重新赋值,这里需要注意,我会对比新老数据,如果新数据为空,老数据不为空,则只是更新时间,不置换value。value随着自己的过期时间结束,改造后的代码如下:
return LOCAL_CACHE.get(key, () -> { String updateKey = getUpdateKey(key); String value = rdbCommonTairCluster.get(key); List<ItemShow> cache = StringUtils.isBlank(cache) ? Collections.emptyList() : JSON.parseObject(value, new TypeReference<List<ItemShow>>(){}); if (rdbCommonTairCluster.exists(updateKey)) { return cache; } rdbCommonTairCluster.set(updateKey, currentTime, cacheUpdateSecond); List<ItemShow> itemShows = getRankingItemOriginal(context, rankingRequest); if (CollectionUtils.isNotEmpty(itemShows)) { rdbCommonTairCluster.set(key, JSON.toJSONString(itemShows), new SetParams().ex(Common- Switch.rankingExpireSecond)); } return itemShows; });
为了使这段代码能够复用,我将该多级缓存抽象出来一个独立对象,代码如下:
public class GatherCache<V> { @Setter private Cache<String, List<V>> localCache; @Setter private CenterCache centerCache; 6 public List<V> get(boolean needCache, String key, @NonNull Callable<List<V>> loader, Func- tion<String, List<V>> parse) { try { // 是否需要是否缓存 return needCache ? localCache.get(key, () -> getCenter(key, loader, parse)) : loader.call(); } catch (Throwable e) { GatherContext.error(this.getClass().getSimpleName() + " get catch exception", e); } return Collections.emptyList(); } private List<V> getCenter(String key, Callable<List<V>> loader, Function<String, List<V>> parse) throws Exception { String updateKey = getUpdateKey(key); String value = centerCache.get(key); boolean blankValue = StringUtils.isBlank(value); List<V> cache = blankValue ? Collections.emptyList() : parse.apply(value); if (centerCache.exists(updateKey)) { return cache; } centerCache.set(updateKey, currentTime, cacheUpdateSecond); List<V> newCache = loader.call(); if (CollectionUtils.isNotEmpty(newCache)) { centerCache.set(key, JSON.toJSONString(newCache), cacheExpireSecond); } return newCache; } }
将从数据源获取数据的代码交与外部实现,使用Callable的形式,同时通过泛型约束数据源类型,这里还有一点瑕 疵还没得到解决,就是通过fastJson转换String到对象时,没法使用泛型直接转,我这里就采用了外部化的处理, 就是跟获取数据源方式一样,由外部来决定如何解析从redis中获取到的字符串value。调用方式如下:
List<ItemShow> itemShowList = gatherCache.get(true, rankingRequest.getKey(), () -> getRankingItemOriginal(rankingRequest, context.getRequestContext()), v -> JSON.parseObject(v, new TypeReference<List<ItemShow>>() {}));
同时我还采用的建造者模式,方便gatherCache类快速生成,代码如下:
@PostConstruct public void init() { this.gatherCache = GatherCacheBuilder.newBuilder() localMaximumSize(500) localExpireAfterWriteSeconds(30) build(rdbCenterCache); 7 }
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(3)https://developer.aliyun.com/article/1338379?groupCode=taobaotech