0

ExpireAfter 只会清除该项目,但不会重新创建该项目。所以我需要做的是,在预定义的时间间隔之后,我需要从缓存中清除特定项目,同时我需要重新创建它。如果数据没有变化,它可能会使用相同的数据重新创建。假设数据已更改,重新创建将提供最新的对象。

我的想法是一直从缓存中检索最新的项目。相比之下,刷新功能(https://github.com/ben-manes/caffeine/wiki/Refresh)将为第一个请求提供陈旧的项目并进行异步加载。因此,对于第二个请求,缓存将提供最新的对象。

  • 在我的情况下,重新获取过期条目的异步删除侦听器应该可以工作。您能否提供一些有关如何实现这一目标的信息?
  • 我也很想知道定时任务是怎么做到的?

假设缓存可以解决以下两种情况:

后续请求案例:

  • 我知道 refreshAfterWrite 将第一次提供过时的条目,但对于第二次请求,如果缓存尚未完成加载过期条目会发生什么?

  • 缓存是否阻塞第二个请求,完成重新获取,然后将最新值提供给第二个请求?

  • 这个想法是让缓存在定义的条目到期时间之后提供最新的数据。

在缓存必须一次性加载等于其容量的值的情况下:

  • 假设缓存大小为 100,加载所有 100 个项目的时间为 2 分钟。

  • 假设第一个请求将同时将 100 个项目加载到缓存中,在定义的到期时间之后,缓存应该驱逐并重新获取所有 100 个元素。

  • 对于从这 100 个项目中访问项目的第二个请求,我怎样才能使缓存足够智能,以便它返回已重新加载的条目并异步重新加载其他条目?

  • 这个想法不是阻止对现有条目的任何请求。为现有条目提供请求并为剩余的过期条目重新加载。

4

1 回答 1

0

在我的情况下,重新获取过期条目的异步删除侦听器应该可以工作。您能否提供一些有关如何实现这一目标的信息?

删除侦听器需要对缓存的引用,但在构造期间不可用。如果它改为调用私有方法,则不会捕获未初始化的字段,并且可以在运行时解析它。

Cache<K, V> cache = Caffeine.newBuilder()
    .expireAfterWrite(1, TimeUnit.HOURS)
    .removalListener((K key, V value, RemovalCause cause) -> {
      if (cause == RemovalCause.EXPIRED) {
        reload(key);
      }
    }).build();

private void reload(K key) {
  cache.get(key, k -> /* load */);
}

我也很想知道定时任务是怎么做到的?

如果您要重新加载所有条目,那么您甚至可能不需要键值缓存。在这种情况下,最简单的方法是重新加载不可变地图。

volatile ImmutableMap<K, V> data = load();

scheduledExecutorService.scheduleAtFixedRate(() -> data = load(), 
    /* initial */ 1, /* period */ 1, TimeUnit.HOURS);

我知道 refreshAfterWrite 将第一次提供过时的条目,但对于第二次请求,如果缓存尚未完成加载过期条目会发生什么?

后续请求获取过时的条目,直到 (a) 刷新完成并更新映射或 (b) 条目被删除并且调用者必须重新加载。如果条目在刷新过程中过期,则可能会出现 (b) 的情况,此时返回陈旧值不再是一种选择。

缓存是否阻塞第二个请求,完成重新获取,然后将最新值提供给第二个请求?

不,会返回陈旧但有效的值。这是为了让刷新隐藏重新加载热门条目的延迟。例如,所有请求都使用的应用程序配置在过期时会阻塞,从而导致周期性延迟。刷新将被提前触发,重新加载,调用者永远不会观察到它不存在。这隐藏了延迟,同时还允许空闲条目过期并消失。

在缓存必须一次性加载等于其容量的值的情况下......在定义的到期时间之后,缓存应该驱逐并重新获取所有 100 个元素。

您的描述中不清楚的部分是缓存是否仅重新加载刷新期间正在访问的条目,或者是否重新加载整个内容。前者是 Caffeine 提供的,而后者最好使用显式调度线程。

于 2021-04-04T18:40:15.030 回答