Java

Guava Cache最佳实践

2019-01-04  本文已影响154人  十毛tenmao

项目中经常使用Guava Cache,根据经验总结了一些最佳实践。

示例代码

快速有效的使用示例如下:

LoadingCache<Integer, Model> modelCache = CacheBuilder.newBuilder()
        //限制缓存大小,防止OOM
        .maximumSize(1000)
        //提供过期策略
        .expireAfterAccess(100, TimeUnit.MINUTES)
        //缓存不存在的时候,自动加载
        .build(new CacheLoader<Integer, Model>() {
            @Override
            public Model load(@Nonnull Integer key) {
                Model model = doGetModel(key);
                if (model == null) {
                    //guava不支持null,会抛出异常InvalidCacheLoadException,最佳办法是抛出自定义异常
                    throw new ModelNotFoundException();
                }
                return model;
            }
        });

最佳实践

自动加载

如果缓存不存在,则自动去数据源加载数据到缓存

.build(new CacheLoader<Integer, Model>() {
    @Override
    public Model load(@Nonnull Integer key) {
        return doGetModel(key);
    }
)

内存控制

使用缓存一定要防止缓存占用过多的内存,导致程序OOM。需要对缓存的内存使用量进行限制,同时还需要设置过期或刷新策略。

//限制缓存大小,防止OOM
.maximumSize(1000)
//提供过期策略
.expireAfterAccess(100, TimeUnit.MINUTES)

上面是使用得最多的两个选项,其他选项还有:

removal listener

在一些场景下,监控cache的换出结果,方便做出响应,比如在集群本地缓存同步的时候,可以监听后同步给集群内其他机器。参见:本地缓存同步的一个简单方案

.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:
            //如果是缓存到期等原因被删除,则需要通知分布式环境下的其他机器也要删除
            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);
    }
})

查看缓存统计值

可以了解缓存使用的特性,比如命中率等

CacheStats Cache#stats();

public final class CacheStats {
  //命中次数
  private final long hitCount;
  //击穿次数
  private final long missCount;
  //加载成功次数
  private final long loadSuccessCount;
  //加载发生异常的次数
  private final long loadExceptionCount;
  //加载时间总机
  private final long totalLoadTime;
  //换出的次数
  private final long evictionCount;
}

不常用功能

weakKey, weakValue, softValue

使用这些值可以把内存的使用量交给JVM来控制,一般不太实用

NULL值的处理

GauvaCache不支持null值的缓存,而且会抛出异常InvalidCacheLoadException,最佳办法是抛出自定义异常,然后在Cache#get的时候捕捉定义异常。示例如下:

@Override
public Model load(@Nonnull Integer key) {
    Model model = doGetModel(key);
    if (model == null) {
        //guava不支持null,会抛出异常InvalidCacheLoadException,最佳办法是抛出自定义异常
        throw new ModelNotFoundException();
    }
    return model;
}
try {
    modelCache.get(key);
} catch (ExecutionException e) {
    //TODO: handle exception
} catch (ModelNotFoundException e) {
    //TODO: handle exception
}

注意事项

参考

上一篇下一篇

猜你喜欢

热点阅读