Java 使用缓存写入程序进行基于缓存时间的逐出

Java 使用缓存写入程序进行基于缓存时间的逐出,java,caching,caffeine,Java,Caching,Caffeine,我正在使用具有以下配置的咖啡因缓存: datesCache = Caffeine.newBuilder() .maximumSize(1000L) .expireAfterWrite(1, TimeUnit.HOURS) .writer(new CacheWriter<String, Map<String, List<ZonedDateTime>>>() {

我正在使用具有以下配置的咖啡因缓存:

datesCache = Caffeine.newBuilder()
                .maximumSize(1000L)
                .expireAfterWrite(1, TimeUnit.HOURS)
                .writer(new CacheWriter<String, Map<String, List<ZonedDateTime>>>() {
                    @Override
                    public void write(@NonNull final String key, @NonNull final Map<String, List<ZonedDateTime>> datesList) {
                        CompletableFuture.runAsync(() -> copyToDatabase(key, datesList), Executors.newCachedThreadPool());
                    }

                    @Override
                    public void delete(@NonNull final String key, @Nullable final Map<String, List<ZonedDateTime>> datesList,
                            @NonNull final RemovalCause removalCause) {
                        System.out.println("Cache key " + key + " got evicted from due to " + removalCause);
                    }
                })
                .scheduler(Scheduler.forScheduledExecutorService(Executors.newSingleThreadScheduledExecutor()))
                .removalListener((key, dateList, removalCause) -> {
                    LOG.info("Refreshing cache key {}.", key);
                    restoreKeys(key);
                })
                .build();
datesCache=Caffeine.newBuilder()
.最大尺寸(1000L)
.expireAfterWrite(1,时间单位为小时)
.writer(新缓存编写器(){
@凌驾
公共无效写入(@NonNull final String key,@NonNull final Map datesList){
CompletableFuture.runAsync(()->copyToDatabase(key,datesList),Executors.newCachedThreadPool());
}
@凌驾
public void delete(@NonNull final String key、@null final Map datesList、,
@非空最终移除原因(移除原因){
System.out.println(“缓存键”+key+”由于“+removalCause”被逐出);
}
})
.scheduler(scheduledexecutorservice的scheduler.forScheduledExecutorService(Executors.newSingleThreadScheduledExecutor())
.removalListener((键、日期列表、removalCause)->{
info(“刷新缓存键{}.”,key);
恢复键;
})
.build();
如果值满足某些条件,我将使用
CacheWriter
在写入缓存时将记录复制到分布式数据库。另外,在逐出时,我使用
RemovalListener
调用带有逐出键的后端服务,以保持记录的最新状态

为了实现这一点,我还必须在启动服务时初始化缓存,并使用
put
方法在缓存中插入值。我使用
datesCache.get(key,datesList->callBackendService(key))
从缓存中检索值,以防请求的是初始化时没有得到的密钥

利用此缓存的API有很长的使用周期,而且似乎出于某种原因记录被逐出(在每个请求中?),因为
RemovalListener
CacheWriter
中的代码每隔几毫秒执行一次,最终创建了25000多个线程并解决了服务错误

有人能告诉我我是否犯了致命的错误吗?或者一些明显的错误?我觉得我对咖啡因有很深的误解


目标是让缓存中的记录每1小时刷新一次,并且在刷新时,从后端API获取新值,如果它们满足某些条件,则将其保存在数据库中。

缓存进入无限循环,因为
RemovalListener
是异步的,因此在重载时,在
RemovalListener
实际刷新缓存之前,缓存值已被对过期密钥的请求所替换

因此,这些值将:

  • 将从缓存中删除,并替换
    删除原因
  • 调用
    RemovalListener
  • 再次刷新并被替换,然后
  • 回到#1
  • 解决方案:
    评估
    RemovalListener
    中的
    RemovalCause
    以忽略
    替换的
    键。方法
    wasExecuted()
    可以使用,也可以自己比较枚举值。

    为什么每次写入都要创建一个新的线程池,而每次写入都不会关闭?嗨,Ben!有趣的是,我不知道一个
    CompletableFuture
    在完成主体工作后不会关闭。服务中还有其他类似的代码,我假设这些行为验证是在那个时候完成的。我肯定可以执行
    CompletableFuture.runAsync(()->copyToDatabase(key,datesList),executor)。然后运行(executor::shutdown)
    ,我肯定会重新查看其中的代码片段。我的问题仍然是,
    RemovalListener
    CacheWriter
    代码为什么会被如此频繁地调用?我的缓存配置正确吗?这些行为如何?我想可能是因为您的删除侦听器恢复了密钥(put?)。如果删除原因被替换,那么您将有一个无限循环。对于线程池,您希望重用它,并且最常用的方法是使用默认的FJP commonPool。是的,它是缓存上的一个
    put
    。我看到了
    RemovalCause
    ,这对我来说开始有意义了。因此,作为我的解决方案的一部分,我应该只在RemovalCause过期时刷新,并且这些值只会被调度程序逐出,对吗?另外,感谢您提供有关如何正确使用线程池的建议。我很感激!是的,那会更好。那个put然后调用writer,顺便说一句,我会将writer删除为无用,然后在缓存更新代码旁边进行写操作。