我可以像 CRON 作业一样安排 Java Spring 缓存在每个小时的顶部过期吗?

Can I schedule Java Spring Cache to expire at the top of each hour like a CRON job?

我目前将其设置为 12 小时后过期。但是,它也会在每个缓存首次写入后 12 小时过期。我希望它只在中午 12 点和中午 12 点刷新。这可能吗?在我的 cacheConfig 文件中,我有:

@Component
@EnableCaching
public class CacheConfig {

   @Bean
   public Caffeine defaultCacheConfig() {
       return Caffeine.newBuilder()
               .expireAfterWrite(12, TimeUnit.HOURS);
   }
}

我正在使用咖啡因缓存库。

我相信Caffeine不支持这种调度。但如果这是强烈的要求并且应该按如下方式实现 - 您可以使用 Spring 的 @Scheduled 注释,它允许使用 Cron 配置。您可以在这里阅读:https://www.baeldung.com/spring-scheduled-tasks

所以对于我的设想,它可以按以下方式工作:

  • 设置计划 Spring 服务并配置所需的 Cron。通过字段或构造函数自动装配 CacheManager 并设置 refreshCache() 以清除 Caffeine 管理器的所有缓存。我将留下一个代码示例,但不确定它是否 100% 有效:)

      @Component
      public class CacheRefreshService {
    
         @Autowired
         private CacheManager cacheManager;
    
         @Scheduled(cron = ...)
         public void refreshCache() {
            cacheManager.getCacheNames().stream()
               .map(CacheManager::getCache)
               .filter(Objects::nonNull)
               .forEach(cache -> cache.clear());
         }
     }
    

并且不要忘记为您的@Configuration-s 添加@EnableScheduling,或者您可以将它添加到@SpringBootApplication 如果您是运行 一个。

Caffeine 支持变量到期,其中条目的持续时间必须独立计算。如果您希望所有条目同时过期,您可以这样写,

Caffeine.newBuilder()
    .expireAfter(new Expiry<K, V>() {
      public long expireAfterCreate(K key, V value, long currentTime) {
        var toMidnight = Duration.between(LocalDate.now(), 
            LocalDate.now().plusDays(1).atStartOfDay());
        var toNoon = Duration.between(LocalTime.now(), LocalTime.NOON);
        return toNoon.isNegative() ? toMidnight.toNanos() : toNoon.toNanos();
      }
      public long expireAfterUpdate(K key, V value, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(K key, V value, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    }).build();

对于这样一个简单的任务,使用过期时间可能有点矫枉过正。相反,如果你想清除缓存,那么计划任务可以代替,正如@alexzander-zharkov 所建议的那样。

@Scheduled(cron = "0 0,12 * * *")
public void clear() {
  cache.invalidateAll();
}

由于这会清空缓存,因此在重新加载条目时会降低性能。相反,您可以异步刷新缓存,以便在不惩罚任何调用者的情况下重新加载条目。

@Scheduled(cron = "0 0,12 * * *")
public void refresh() {
  cache.refreshAll(cache.asMap().keySet());
}