0

使用Caffeine 2.8.1和 Java 8。我创建了LoadingCache<String, Boolean>. 我正在加载缓存,cache.putAll(getAllKeyValues())其中getAllKeyValues()返回一个Map<String, Boolean>.

我已经指定了一个CacheLoader来通过调用keyExistsOnServer(key)调用外部服务来获取值的方法来计算值。

我已经指定了一个自定义过期策略,如果该值设置为false在缓存中创建/更新条目时,我想在 10 分钟内从缓存中逐出该条目。并保持它只要Long.MAX_VALUE值是true

  private RemovalListener<String, Boolean> removeListener =
      (key, value, cause) ->
          logger.info(
              "Entry for Key={} with value={} was removed ({}) from cache", key, value, cause);
  private LoadingCache<String, Boolean> cache =
      Caffeine.newBuilder()
          .expireAfter(
              new Expiry<String, Boolean>() {
                private static final long EXPIRATION_TEN_MINS_IN_NANOSECONDS = 600000000000L;

                @Override
                public long expireAfterCreate(String key, Boolean value, long currentTime) {
                  // If value is false that means key does not exist, so set expiration to 10 mins
                  return value ? Long.MAX_VALUE : EXPIRATION_TEN_MINS_IN_NANOSECONDS;
                }

                @Override
                public long expireAfterUpdate(
                    String key, Boolean value, long currentTime, long currentDuration) {
                  // If value is changed from true to false then set expiration to 10 mins
                  return value ? Long.MAX_VALUE : EXPIRATION_TEN_MINS_IN_NANOSECONDS;
                }

                @Override
                public long expireAfterRead(
                    String key, Boolean value, long currentTime, long currentDuration) {
                  // Don't modify expiration time after read
                  return currentDuration;
                }
              })
          .recordStats()
          .removalListener(removeListener)
          .build(key -> keyExistsOnServer(key));


问题#1:根据我想要实现的目标,我的到期政策看起来是否正确?

问题2:

我没有看到RemovalListener根据驱逐政策被召唤。这可能是由于此github 问题中所述的清理任务的累积。

但是,我的代码的正确性取决于这样一个事实,即一旦false一个条目的过期持续时间(如果是值,则为 10 分钟)已经过去,如果我们调用,cache.get(key)那么它不应该从缓存中返回过期值,而是调用CacheLoaderkeyExistsOnServer( key)方法来获取值。有人可以断言这就是它的行为方式吗?

这是@Louis Wasserman 在 github 问题上所说的,但我不清楚这是否能澄清它:

实际上,发生的情况是 get 调用本身发现条目已过期并将其添加到要清理的条目队列中

问题#3:如果调用时RuntimeException抛出了CacheLoaderkeyExistsOnServer (key)方法会发生cache.get(key)什么?

问题#4cache.asMap()是否包含已过期但由于应计清理而未被驱逐的条目?

问题#5:当我currentDurationexpireAfterRead()方法中记录时,它似乎与经过的时间不一致。即,如果将其设置为6000000000000(10 分钟)并更新该值,false我预计 5 分钟后它应该是300000000000,但事实并非如此。为什么这样?

4

1 回答 1

1

问题#1:根据我想要实现的目标,我的到期政策看起来是否正确?

是的,这看起来不错。您可以将常量 aDurationTimeUnit用于计算,只是为了让它看起来更漂亮。

问题#2:我没有看到根据驱逐政策调用 RemovalListener。

当缓存上发生足够的活动时,就会发生这种情况。您可以根据下一个条目设置为过期的时间来指定Scheduler将唤醒并为您呼叫的对象。cache.cleanUp对于 Java 9+ 用户,Java 提供了一个内置的调度线程,您可以Caffeine.scheduler(Scheduler.systemScheduler)在构建缓存时添加该线程。

一旦一个条目的过期持续时间已经过去,如果我们调用 cache.get(key) 那么它不应该从缓存中返回过期值,而是调用 CacheLoader。

正确的。缓存将在查找时验证条目,如果已​​过期,则重新加载它。它永远不会返回过期的条目。

问题#3:如果调用cache.get(key)的keyExistsOnServer(key)方法CacheLoader抛出RuntimeException怎么办?

映射不会建立,异常会传播给调用者。下一次调用get(key)将重新尝试,这可能会再次失败。如何适应这一点是您的选择。通常不是这样是一个好的答案,或者有时缓存它失败是一个更好的答案。

问题#4:cache.asMap() 是否会包含已过期但由于应计清理而未被驱逐的条目?

缓存将保存条目,但不显示它们,例如通过查找或迭代。唯一的外部指示是,size()因为在删除过期条目之前该计数器不会更新。

问题#5:当我在 expireAfterRead() 方法中记录 currentDuration 时,它似乎与经过的时间不一致。即,如果将其设置为 600000000000(10 分钟)并将值更新为 false,我预计 5 分钟后它应该是 300000000000,但事实并非如此。为什么这样?

请使用单元测试打开一个问题,我们可以诊断。

于 2021-03-04T05:57:12.810 回答