在Spring Boot中,使用Caffeine是否可以为每个缓存设置不同的规范?

45

我有一个简单的Spring Boot应用程序,使用Spring Boot 1.5.11.RELEASE版本,并在应用程序的Configuration类上使用@EnableCaching

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

应用程序属性

spring.cache.type=caffeine
spring.cache.cache-names=cache-a,cache-b
spring.cache.caffeine.spec=maximumSize=100, expireAfterWrite=1d

问题

我的问题很简单,如何针对每个缓存指定不同的大小/过期时间。例如,也许cache-a可以有效期为1天。但是cache-b可能需要1周。咖啡因缓存的规范似乎是全局适用于CacheManager而不是Cache本身。我有什么遗漏吗?也许有更适合我用例的提供者?


3
我们复制了Guava适配器,但我不知道为什么它被限制在全局配置中。有一个拉取请求来添加这个功能。过去,Spring团队建议使用Java配置作为解决方法。可以试着问问@snicoll。 - Ben Manes
@BenManes 谢谢您,您能否给我指出一个使用Java配置的示例吗?它仍然使用Caffeine作为底层管理器吗?实际上,我并不在意使用哪种实现方式,我“只是”想要一个可以做到这一点的实现。 - David
3
也许这些链接中的一个可以帮到您?123 - Ben Manes
2
@Stephane Nicoll的最有用的答案:将多个Caffeine LoadingCaches添加到Spring CaffeineCacheManager中 - Grigory Kislin
4个回答

29

这是你唯一的机会:

@Bean
public CaffeineCache cacheA() {
    return new CaffeineCache("CACHE_A",
            Caffeine.newBuilder()
                    .expireAfterAccess(1, TimeUnit.DAYS)
                    .build());
}

@Bean
public CaffeineCache cacheB() {
    return new CaffeineCache("CACHE_B",
            Caffeine.newBuilder()
                    .expireAfterWrite(7, TimeUnit.DAYS)
                    .recordStats()
                    .build());
}

只需将您的自定义缓存公开为Bean。 它们会自动添加到 CaffeineCacheManager 中。


对于 @Cacheable 的使用,我必须手动使用 cacheManager.registerCustomCache(name, cache.getNativeCache()); 将它们注册到 CacheManager 中。 - pards
2
请检查您的配置中是否有@EnableCaching。不需要手动注册缓存。 - membersound
1
@membersound,如果您有多个CaffeineCacheManager该怎么办? - Katy

5
我这样配置了多个缓存管理器:

我这样配置了多个缓存管理器

@Primary
@Bean
public CacheManager template() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE_TEMPLATE);
    cacheManager.setCaffeine(caffeineCacheBuilder(this.settings.getCacheExpiredInMinutes()));
    return cacheManager;
}

@Bean
public CacheManager daily() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE_TEMPLATE);
    cacheManager.setCaffeine(caffeineCacheBuilder(24 * 60));
    return cacheManager;
}

正常使用缓存

@Cacheable(cacheManager = "template")
@Override
public ArrayList<FmdModel> getData(String arg) {
    return ....;
}

更新

看起来以上代码有一个大错误,所以我进行了更改,改为:

@Configuration
@Data
@Slf4j
@ConfigurationProperties(prefix = "caching")
public class AppCacheConfig {


    //This cache spec is load from `application.yml` file
    // @ConfigurationProperties(prefix = "caching")
    private Map<String, CacheSpec> specs;

    @Bean
    public CacheManager cacheManager(Ticker ticker) {
        SimpleCacheManager manager = new SimpleCacheManager();
        if (specs != null) {
            List<CaffeineCache> caches = specs.entrySet().stream()
                    .map(entry -> buildCache(entry.getKey(), entry.getValue(), ticker)).collect(Collectors.toList());
            manager.setCaches(caches);
        }
        return manager;
    }

    private CaffeineCache buildCache(String name, CacheSpec cacheSpec, Ticker ticker) {
        log.info("Cache {} specified timeout of {} min, max of {}", name, cacheSpec.getTimeout(), cacheSpec.getMax());
        final Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .expireAfterWrite(cacheSpec.getTimeout(), TimeUnit.MINUTES).maximumSize(cacheSpec.getMax())
                .ticker(ticker);
        return new CaffeineCache(name, caffeineBuilder.build());
    }

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

这个AppCacheConfig类允许您根据需要定义多个缓存规范。您可以在application.yml文件中定义缓存规范。

caching:
  specs:
    template:
      timeout: 10 #15 minutes
      max: 10_000
    daily:
      timeout: 1440 #1 day
      max: 10_000
    weekly:
      timeout: 10080 #7 days
      max: 10_000
    ...:
      timeout: ... #in minutes
      max:
      

然而,这个类有一个限制,我们只能设置timeoutmax大小。因为CacheSpec类的存在。

@Data
public class CacheSpec {

    private Integer timeout;
    private Integer max = 200;

}

因此,如果您想添加更多的配置参数,您需要在CacheSpec类上添加更多参数,并在AppCacheConfig.buildCache函数上设置Cache配置。
希望这可以帮到您!

什么是大错误?在这里它似乎对我有效...我将一个bean定义为主要的,然后另一个管理器必须像你一样在可缓存注释中吗..? - RichieK
感谢这个实现,我不知道每个人是否都需要它,但在我的情况下,我必须在资源/application.properties中添加 spring.main.allow-bean-definition-overriding=true此外,在配置示例中,您将timeout: 10 #15分钟放置了进去,但实际上是10分钟。 - Sebastian Zapata

5

你可以使用CaffeineCacheManager的registerCustomCache()方法来替代SimpleCacheManager。以下是一个示例:

CaffeineCacheManager manager = new CaffeineCacheManager();

manager.registerCustomCache(
    "Cache1",
    Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterAccess(6, TimeUnit.MINUTES)
        .build()
);

manager.registerCustomCache(
    "Cache2",
    Caffeine.newBuilder()
        .maximumSize(2000)
        .expireAfterAccess(12, TimeUnit.MINUTES)
        .build()
);

3
我将我的初始PR转换成了一个单独的微型项目
要开始使用它,只需从Maven Central添加最新的依赖项。
<dependency>
    <groupId>io.github.stepio.coffee-boots</groupId>
    <artifactId>coffee-boots</artifactId>
    <version>2.0.0</version>
</dependency>

属性的格式如下:

coffee-boots.cache.spec.myCache=maximumSize=100000,expireAfterWrite=1m

如果没有定义特定的配置,CacheManager 将默认使用 Spring 的行为。


1
谢谢。我正准备开始编写一些自定义代码,而这正好解决了我的问题。 - chrylis -cautiouslyoptimistic-

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接