java - Guava Cache 的概率提前过期

标签 java guava google-guava-cache

我有一个使用 expireAfterWrite(20, TimeUnit.MINUTES) 创建的 Guava 缓存。缓存在创建时也会使用数十个条目进行初始化。当值过期时,客户端应该调用外部服务并刷新缓存。

问题是 - 所有初始化值都会在 20 分钟后同时过期,并且客户端几乎会在同一个实例中调用该服务数十次。

我不希望这种事发生。为了避免这种情况 - 一个想法是 probabilistically expire the entries比 TTL 早一点,这样服务就不会同时受到大量攻击。

不幸的是,我没有看到使用 Guava 缓存执行此操作的选项 - 至少 Wiki 或 Javadoc 中没有弹出任何内容。还有其他可用的图书馆吗?我试图避免为此目的编写自己的缓存实现。

最佳答案

缓存预热是一个相当复杂的主题,因此有很多合理的方法,但不一定是一种“正确”的方法。一个简单的选择是在未来的交错时间触发额外的写入,以重置到期时间:

public static <K, V> void staggerCacheExpiration(
    Cache<K, V> cache, long maxExpiration, TimeUnit unit, ScheduledExecutorService scheduler) {
  for (Entry<K, V> e : cache.asMap().entrySet()) {
    long delay = ThreadLocalRandom.current().nextLong(0, maxExpiration);
    scheduler.schedule(() -> cache.put(e.getKey(), e.getValue()), delay, unit);
  }
}

任何依赖于Cache本身的方法的一个关键问题是,只有当调用者需要该值时,您最终才会触发(可能昂贵的)刷新。最好不要使用 expireAfterWrite()refreshAfterWrite(),而是运行一个负责按顺序更新每个键的专用线程。通过使用单个线程更新所有键,您自然会避免同时刷新多个键的任何热点,并且还可以避免阻塞依赖于缓存中的值的任何线程。

正如 Ben Manes 建议的那样,您可能更愿意将 Cache 封装在您自己的类型中,这样您选择的任何行为都不会暴露给调用者。

关于java - Guava Cache 的概率提前过期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39927094/

相关文章:

java - 使用一个类创建并运行多个 Java 窗口

java - Guava Spliter 返回一个 Collection,但我想要一个 HashMap

java - 限制将超大对象放入 Guava 缓存

java - 对调用静态方法的类进行单元测试

java - Maven 循环依赖解析

java - 延迟与解耦架构建议

java - 在 Guava 的帮助下使用另一个 map 变换 map

java - Guava CacheBuilder 提前驱逐项目

java - 运行用户提供的代码的控件库是否应该拦截异常或 Throwable?

java - Java中如何获取泛型接口(interface)实现的方法对象