【问题标题】:How to use @Cacheable with Kotlin suspend funcion如何将 @Cacheable 与 Kotlin 挂起功能一起使用
【发布时间】:2020-10-15 13:26:09
【问题描述】:

我在 Kotlin 和 Spring Boot 项目中工作,我正在尝试使用 Caffeine 进行缓存。我有一个带有暂停功能的服务,可以进行 http 调用。这是我的配置:

@Bean
open fun caffeineConfig(): @NonNull Caffeine<Any, Any> {
   return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.SECONDS)
}

@Bean
open fun cacheManager(caffeine: Caffeine<Any, Any>): CacheManager {
    val caffeineCacheManager = CaffeineCacheManager()
    caffeineCacheManager.getCache("test")
    caffeineCacheManager.setCaffeine(caffeine)
    return caffeineCacheManager
}

这是我要缓存的函数:

@Cacheable(value = ["test"])
open suspend fun getString(id: String): String {
    return client.getString(id)
}

但似乎缓存不起作用,因为我可以从日志中看到每次调用服务函数时都会调用客户端。 @Cacheable 是否不适用于挂起功能?还是我错过了什么?

【问题讨论】:

    标签: kotlin caching kotlin-coroutines caffeine


    【解决方案1】:

    @Cacheable 的文档说:

    每次调用建议的方法时,都会应用缓存行为,检查是否已经为给定的参数调用了该方法。合理的默认值只是使用方法参数来计算密钥,但可以通过 key() 属性提供 SpEL 表达式,或者自定义 KeyGenerator 实现可以替换默认值(参见 keyGenerator())。

    suspend 修饰符在生成的代码中插入一个Continuation&lt;String&gt; 参数,该参数接受来自调用者的输入。这大概意味着每次调用都有自己的延续,并且缓存将其检测为唯一调用。

    但是,由于返回值也会根据延续而改变,因此您不能让缓存忽略延续参数。更好的方法是不使用suspend 函数,而是返回消费者可以共享的Deferred

    @Cacheable(value = ["test"])
    open fun getString(id: String): Deferred<String> {
        return someScope.async {
            client.getString(id)
        }
    }
    
    // Consumer side
    getString(id).await()
    

    这应该适用于标准缓存机制,因为Deferred 是一个普通对象,不需要特殊参数。

    【讨论】:

    • 这成功了!感谢您的帮助和非常好的解释!
    猜你喜欢
    • 2020-02-22
    • 2020-02-23
    • 2020-12-29
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    • 2018-03-15
    • 2017-01-16
    • 2016-07-03
    相关资源
    最近更新 更多