一. 定义缓存
/**
* 缓存
* K:Pair<String, String> 集群名称,逻辑队列
* V:String 物理队列
*/
public LoadingCache<Pair<String, String>, String> clusterPhysicalQueueCache = CacheBuilder.newBuilder()
.initialCapacity(initialCapacity)//初始容量
.maximumSize(maximumSize)//最大容量
.expireAfterWrite(expireAfterWrite,TimeUnit.SECONDS)//过期时间
.recordStats()//开启统计
.build(
new CacheLoader<Pair<String, String>, String>() {
@Override
public String load(Pair<String, String> key) throws Exception {
return getPhysicalFromDB(key);//查询数据库
}
}
);
1.1 缓存过期清理方式
首先看一下三种基于时间的清理或刷新缓存数据的方式:
expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。
expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收。
refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。
考虑到时效性,我们可以使用expireAfterWrite,使每次更新之后的指定时间让缓存失效,然后重新加载缓存。guava cache会严格限制只有1个加载操作,这样会很好地防止缓存失效的瞬间大量请求穿透到后端引起雪崩效应。
然而,通过分析源码,guava cache在限制只有1个加载操作时进行加锁,其他请求必须阻塞等待这个加载操作完成;而且,在加载完成之后,其他请求的线程会逐一获得锁,去判断是否已被加载完成,每个线程必须轮流地走一个“”获得锁,获得值,释放锁“”的过程,这样性能会有一些损耗。这里由于我们计划本地缓存1秒,所以频繁的过期和加载,锁等待等过程会让性能有较大的损耗。
1.2 load()方法
什么时候被调用:从缓存获取不到数据,触发此方法,查询数据库,并将查询到的的数据添加到缓存里。
注意:不能return null,否则会报空指针异常。如果不需要进行查询数据库的操作,可以返回一个字符串"null"以防止它不报错。
1.3 recordStats()记录状态
clusterPhysicalQueueCache.stats().hitRate():获得命中率
clusterPhysicalQueueCache.stats().averageLoadPenalty():加载新的缓存的平均时长
1.4 build方法:自己定义load方法,加载获取缓存的方式
二. get和put方法获取缓存、添加缓存
/**获取物理队列
* @param clusterQueue 集群,逻辑队列
* @return
* @throws ExecutionException
*/
public String getPhysicalFromCache(Pair<String, String> clusterQueue) throws ExecutionException {
String physicalName = clusterPhysicalQueueCache.get(clusterQueue);
return physicalName;
}
/**加入缓存
* @param clusterQueue 集群,逻辑队列
* @param physical 物理队列
*/
public void putCache(Pair<String, String> clusterQueue, String physical) {
clusterPhysicalQueueCache.put(clusterQueue, physical);
}
关于缓存的原理分析,可以看这篇https://blog.csdn.net/abc86319253/article/details/53020432