В Spring CaffeineCacheManager добавлено несколько загрузочных кэшей кофеина

Я хочу добавить несколько различных LoadingCacheк весне CacheManagerОднако я не понимаю, как это возможно, используя CaffeineCacheManager, Похоже, что для обновления контента возможен только один загрузчик, однако мне нужны отдельные загрузчики для каждого кэша. Можно ли добавить несколько загрузочных кешей в диспетчер кеша Spring? Если так, то как?

CaffeineCacheManager cacheManage = new CaffeineCacheManager();

LoadingCache<String, Optional<Edition>> loadingCache1 = 
            Caffeine.newBuilder()
            .maximumSize(150)
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .build(test -> this.testRepo.find(test));

LoadingCache<String, Optional<Edition>> loadingCache2 = 
            Caffeine.newBuilder()
            .maximumSize(150)
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .build(test2 -> this.testRepo.find2(test2));

// How do I add to cache manager, and specify a name?

4 ответа

Решение

Да, это возможно. Поскольку вам необходимо точно настроить каждый кэш, вы, вероятно, лучше определите их сами. Возвращаясь к вашему примеру, следующим шагом будет:

SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
    new CaffeineCache("first", loadingCache1),
    new CaffeineCache("second", loadingCache2)));

И тогда вы можете использовать это, как обычно, например,

@Cacheable("first")
public Foo load(String id) { ... }

Если вы используете Spring Boot, вы можете просто выставить отдельный кеш как бины (так org.springframework.cache.Cache реализации), и мы обнаружим их и создадим SimpleCacheManager автоматически для вас.

Обратите внимание, что эта стратегия позволяет использовать абстракцию кэша с различными реализациями. first может быть кофеин тайник и second кеш от другого провайдера.

Наличие этого класса позволит вам использовать @Cacheable("cacheA") где вы хотите как обычно:

      @EnableCaching
@Configuration
public class CacheConfiguration {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.registerCustomCache("cacheA", defaultCache());
        manager.registerCustomCache("cacheB", bigCache());
        manager.registerCustomCache("cacheC", longCache());
        // to avoid dinamic caches e be sure each name is assigned to a specific config (dynamic = false)
        // throws error when tries to use a new cache
        manager.setCacheNames(Collections.emptyList());
        return manager;
    }

    private static Cache<Object, Object> defaultCache() {
        return Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
    }

    private static Cache<Object, Object> bigCache() {
        return Caffeine.newBuilder()
                .maximumSize(5000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
    }

    private static Cache<Object, Object> longCache() {
        return Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(1, TimeUnit.HOURS)
                .build();
    }
}

Спасибо за @rado, это улучшенная версия его ответа. Таким образом, мы можем настроить кеш напрямую из свойств приложения.

      cache:
  specs:
    big-cache:
      expire-after: WRITE
      timeout: 2h
      max-size: 1000
    long-cache:
      expire-after: ACCESS
      timeout: 30d
      max-size: 100

Нам нужны свойства кеша для этого

      @Data
@EnableConfigurationProperties
@Configuration
@ConfigurationProperties(prefix = "cache")
public class CacheProperties {

    private static final int DEFAULT_CACHE_SIZE = 100;

    private Map<String, CacheSpec> specs = new HashMap<>();

    @Data
    public static class  CacheSpec {
        private Duration timeout;
        private Integer maxSize = DEFAULT_CACHE_SIZE;
        private ExpireAfter expireAfter = ExpireAfter.WRITE;
    }

    enum ExpireAfter { WRITE, ACCESS }
}

И тогда мы можем настроить непосредственно из внешнего файла конфигурации

      @EnableCaching
@Configuration
@RequiredArgsConstructor
public class CacheConfiguration {

    private final CacheProperties cacheProperties;

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();

        Map<String, CacheProperties.CacheSpec> specs = cacheProperties.getSpecs();
        specs.keySet().forEach(cacheName -> {
            CacheProperties.CacheSpec spec = specs.get(cacheName);
            manager.registerCustomCache(cacheName, buildCache(spec));
        });

        // to avoid dynamic caches and be sure each name is assigned
        // throws error when tries to use a new cache
        manager.setCacheNames(Collections.emptyList());
        return manager;
    }

    private Cache<Object, Object> buildCache(CacheProperties.CacheSpec cacheSpec) {
        if (cacheSpec.getExpireAfter() == CacheProperties.ExpireAfter.ACCESS) {
            return Caffeine.newBuilder()
                    .expireAfterAccess(cacheSpec.getTimeout())
                    .build();
        }
        return Caffeine.newBuilder()
                .expireAfterWrite(cacheSpec.getTimeout())
                .build();
    }
}

Теперь вы можете использовать кеш с использованием имени кеша

          @Cacheable(cacheNames = "big-cache", key = "{#key}", unless="#result == null")
    public Object findByKeyFromBigCache(String key) {
        // create the required object and return
    }

    @Cacheable(cacheNames = "long-cache", key = "{#key}", unless="#result == null")
    public Object findByKeyFromLongCache(String key) {
        // create the required object and return
    }

Обязательно создайте свой собственный Caffeine Cache, используяcom.github.benmanes.caffeine.cache.Ticker.

Это рабочий пример, протестированный с использованием Java 17, Spring Boot 2.7.7 и Caffeine 3.1.6, где мы настраиваем кэшOne со сроком действия 60 секунд и кэш2, срок действия которого истекает через один час или 3600 секунд:

      @Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManagerTicker(Ticker ticker) {

        var cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(List.of(
                this.buildCache("cacheOne", ticker, 1, 60, TimeUnit.SECONDS),
                this.buildCache("cacheTwo", ticker, 1, 3600, TimeUnit.SECONDS)
        ));

        return cacheManager;
    }

    private CaffeineCache buildCache(String cacheName, Ticker ticker,
                                     int maxSize, int expireAfterWrite, TimeUnit timeUnit) {

        Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
        if (expireAfterWrite > 0) {
            cacheBuilder.expireAfterWrite(expireAfterWrite, timeUnit);
        }
        if (maxSize > 0) {
            cacheBuilder.maximumSize(maxSize);
        }

        cacheBuilder.ticker(ticker);
        return new CaffeineCache(cacheName, cacheBuilder.build());
    }

    @Bean
    public Ticker ticker() {
        return Ticker.systemTicker();
    }
}

Этот пример был адаптирован из раздела «Определение нескольких конфигураций кешей с помощью Spring и Caffeine», где Бен Мейнс указывает, что существует адаптер под названием Coffee Boots, который обеспечивает запрошенное поведение: https://github.com/stepio/coffee-boots

Другие вопросы по тегам