当前位置: 首页 > 知识库问答 >
问题:

咖啡因加载缓存-驱逐与自定义到期策略

蓝昊天
2023-03-14

使用咖啡因2.8.1和Java8。我已经创建了加载缓存

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

我指定了一个自定义过期策略,如果在缓存中创建/更新条目时将该值设置为false,我希望在10分钟内从缓存中逐出该条目。并尽可能长地保存它。最大值如果值为

  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分钟)已经过去,并且如果我们调用缓存。get(key)则不应从缓存返回过期值,而是调用CacheLoader即keyExistsOnServer(key)方法来获取值。有人能断言这就是它的表现吗?

以下是@路易斯·乏色曼在github问题上所说的话,但我不清楚这是否澄清了它:

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

问题#3:如果CacheLoader抛出RuntimeException会发生什么情况,这是调用缓存时的keyExistsOnServer(key)方法。获取(密钥)

问题#4:cache.asMap()是否包含过期但由于权责发生而未被清除的条目?

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

共有1个答案

慕容烨
2023-03-14

问题#1:基于我想要实现的目标,我的过期策略看起来是否正确?

是的,这看起来不错。您可以将常量设为Duration,或者使用TimeUnit进行计算,只是为了让它看起来更漂亮。

问题#2:我看不到拆迁监听者按照拆迁政策被叫来。

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

一旦一个条目的过期时间过去,如果我们调用缓存。get(key),则它不应从缓存返回过期值,而是调用CacheLoader。

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

问题#3:如果CacheLoader抛出运行时异常,该异常是调用缓存时的keyExistsOnServer(key)方法。拿到钥匙了吗?

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

问题#4:cache.asMap()是否包含过期但由于权责发生而未被清除的条目?

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

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

请打开单元测试问题,我们可以进行诊断。

 类似资料:
  • 我试图使用Spring Boot与咖啡因和一些注释函数。在我们的测试中,对控制器endpoint的异步调用出现了一些问题,这些问题似乎与我们使用非异步缓存的事实有关。 在做一些研究时,我看到了很多使用Caffeine手动使用的例子,但是在和Spring Boot和注释中找不到任何东西。看起来和具有非常不同的API。是否可以异步使用默认的Spring Boot? 谢谢

  • 我计划使用infinispan作为我的Hibernate应用程序二级缓存。My all entities具有生命周期属性[新建- 简单地说,当实体生命周期属性状态到达已完成状态时,我想从缓存中驱逐实体。据我所知,Infinispan不支持自定义驱逐策略。处理我的情况的最佳方式是什么?

  • 我已经使用咖啡因缓存设置了一个场景,但我无法让它工作,当参数相同时,总是调用真正的方法。这是我的配置: 波姆。xml CacheManager的配置类 然后是具有可缓存方法的类: 我还尝试将缓存名称添加到可缓存批注: 并将移动到Spring Boot主应用程序类。 真正的方法总是被调用的。 你知道我做错了什么吗? 谢谢

  • 我目前正在学习咖啡因,总体来说我对Spring还不熟悉。我一直在尝试用咖啡因实现缓存;然而,我发现自己遇到了几个问题。 我看到了两种配置咖啡因缓存的方法。 首先是Java类: 第二是在应用范围内。财产: 我想问一下,这两者之间有什么不同之处吗?我需要课程和应用程序吗。属性配置,还是仅其中一个?此外,在Java类实现中,cacheManager是仅应用于名为“example”的缓存,还是应用于每个缓

  • 我在java Play Framework作业中使用Caffeine cache实现来缓存传出的http请求,我看到,尽管缓存已经启动并运行,但我的服务仍然每次都访问外部服务器,而不是返回缓存中的值。我看到记录在日志中的键总是相同的。 所以我看到没有设置,所以这可能意味着缓存立即过期。但我仍然不知道如何在配置中设置它 我没有更改我的java代码,如果我需要这样做,我找不到任何示例,或者我只应该有一

  • 从Java8开始,我们可以在ConcurrentHashMap上使用。compute*方法来按键同步处理,这样,如果两个线程同时在同一个键上执行。compute*方法,回调仍然会相继执行,而不是同时执行。但是ConcurrentHashMap不能像缓存通常允许的那样提供及时删除数据的能力。 Guava/Caffeine缓存提供了基于时间的自动删除值的能力,但是您没有基于键的同步处理的讨厌特性,如在