項(xiàng)目中經(jīng)常使用Guava Cache,根據(jù)經(jīng)驗(yàn)總結(jié)了一些最佳實(shí)踐哪怔。
示例代碼
快速有效的使用示例如下:
LoadingCache<Integer, Model> modelCache = CacheBuilder.newBuilder()
//限制緩存大小廷痘,防止OOM
.maximumSize(1000)
//提供過期策略
.expireAfterAccess(100, TimeUnit.MINUTES)
//緩存不存在的時(shí)候笋额,自動(dòng)加載
.build(new CacheLoader<Integer, Model>() {
@Override
public Model load(@Nonnull Integer key) {
Model model = doGetModel(key);
if (model == null) {
//guava不支持null兄猩,會(huì)拋出異常InvalidCacheLoadException鉴未,最佳辦法是拋出自定義異常
throw new ModelNotFoundException();
}
return model;
}
});
最佳實(shí)踐
自動(dòng)加載
如果緩存不存在铜秆,則自動(dòng)去數(shù)據(jù)源加載數(shù)據(jù)到緩存
.build(new CacheLoader<Integer, Model>() {
@Override
public Model load(@Nonnull Integer key) {
return doGetModel(key);
}
)
內(nèi)存控制
使用緩存一定要防止緩存占用過多的內(nèi)存,導(dǎo)致程序OOM核蘸。需要對(duì)緩存的內(nèi)存使用量進(jìn)行限制啸驯,同時(shí)還需要設(shè)置過期或刷新策略。
//限制緩存大小徙鱼,防止OOM
.maximumSize(1000)
//提供過期策略
.expireAfterAccess(100, TimeUnit.MINUTES)
上面是使用得最多的兩個(gè)選項(xiàng)袱吆,其他選項(xiàng)還有:
-
maximumWeight
:限制最大權(quán)重(權(quán)重的計(jì)算方式需要傳遞Weigher
) -
expireAfterWrite
:寫入后多長時(shí)間過期 -
refreshAfterWrite
:寫入后多長時(shí)間刷新
removal listener
在一些場景下距淫,監(jiān)控cache的換出結(jié)果,方便做出響應(yīng)处铛,比如在集群本地緩存同步的時(shí)候拐揭,可以監(jiān)聽后同步給集群內(nèi)其他機(jī)器堂污。參見:本地緩存同步的一個(gè)簡單方案
.removalListener((RemovalListener<Integer, Model>) notification -> {
log.info("remove taskbot from guava cache: key[{}], cause[{}]", notification.getKey(), notification.getCause());
final RemovalCause cause = notification.getCause();
switch (cause) {
case EXPIRED:
log.info("model evicted because of expiration in guava cache: {}", notification.getKey());
break;
case SIZE:
log.info("model evicted because of size in guava cache: {}", notification.getKey());
break;
case COLLECTED:
//如果是緩存到期等原因被刪除盟猖,則需要通知分布式環(huán)境下的其他機(jī)器也要?jiǎng)h除
log.info("model evicted because of gc in guava cache: {}", notification.getKey());
break;
case EXPLICIT:
log.info("model evicted because of explicit in guava cache: {}", notification.getKey());
break;
case REPLACED:
log.info("model updated because of replaced in guava cache: {}", notification.getKey());
break;
default:
log.error("there should not be [{}]", cause);
}
})
查看緩存統(tǒng)計(jì)值
可以了解緩存使用的特性,比如命中率等
CacheStats Cache#stats();
public final class CacheStats {
//命中次數(shù)
private final long hitCount;
//擊穿次數(shù)
private final long missCount;
//加載成功次數(shù)
private final long loadSuccessCount;
//加載發(fā)生異常的次數(shù)
private final long loadExceptionCount;
//加載時(shí)間總機(jī)
private final long totalLoadTime;
//換出的次數(shù)
private final long evictionCount;
}
不常用功能
weakKey, weakValue, softValue
使用這些值可以把內(nèi)存的使用量交給JVM來控制反镇,一般不太實(shí)用
NULL值的處理
GauvaCache不支持null值的緩存歹茶,而且會(huì)拋出異常InvalidCacheLoadException你弦,最佳辦法是拋出自定義異常,然后在Cache#get的時(shí)候捕捉定義異常尸昧。示例如下:
@Override
public Model load(@Nonnull Integer key) {
Model model = doGetModel(key);
if (model == null) {
//guava不支持null旷偿,會(huì)拋出異常InvalidCacheLoadException,最佳辦法是拋出自定義異常
throw new ModelNotFoundException();
}
return model;
}
try {
modelCache.get(key);
} catch (ExecutionException e) {
//TODO: handle exception
} catch (ModelNotFoundException e) {
//TODO: handle exception
}
注意事項(xiàng)
- GauvaCache異步刷新緩存衷蜓,不會(huì)阻塞線程獲取緩存內(nèi)容(老的內(nèi)容)
- GauvaCache不支持緩存null值