Scala play cache——根据未来的价值来记忆过期的未来
给定以下函数Scala play cache——根据未来的价值来记忆过期的未来,scala,caching,asynchronous,playframework-2.0,Scala,Caching,Asynchronous,Playframework 2.0,给定以下函数 val readV : String => Future[V] val isExpired: V => Boolean 如何使用play cache(或其他方法)记忆readV的结果,直到被显示 我是这样做的: def getCached(k: String) = Cache.getAs[Future[V]](k) def getOrRefresh(k: String) = getCached(k).getOrElse { this.synchroni
val readV : String => Future[V]
val isExpired: V => Boolean
如何使用play cache(或其他方法)记忆readV
的结果,直到被显示
我是这样做的:
def getCached(k: String) = Cache.getAs[Future[V]](k)
def getOrRefresh(k: String) = getCached(k).getOrElse {
this.synchronized {
getCached(k).getOrElse {
val vFut = readV(k)
Cache.set(k, vFut)
vFut
}
}
}
def get(k: String) = getOrRefresh(k).flatMap {
case v if !isExpired(v) => Future.successful(v)
case _ =>
Cache.remove(k)
getOrRefresh(k)
}
这太复杂了,无法确保正确性
有没有更简单的解决办法
google有一个很好的缓存实现-(Guava cache)
在我们的项目中,我们使用的是ScalaCache,它抽象了缓存层,默认情况下允许在一些超时后使值过期
以下是我们的代码示例:
private val underlyingCache = CacheBuilder.newBuilder().expirationAfterAccess(1, TimeUnit.HOURS).build[String, Object]
implicit val cache = ScalaCache(
cache = GuavaCache(underlyingCache),
memoization = MemoizationConfig(toStringConvertor = ParamsToStringConverter))
def getCachedByID(id: String)(implicit format: Format[T]): Option[T] = memoize (cacheTimeout) {
super.getByID(id)(format)
}
在本例中,memoize是一个宏调用,用于处理生成正确的处理。默认情况下,基础guava缓存配置为在1小时后使值过期,但它可以被memoize中的cacheTimeout参数覆盖
它不一定需要是番石榴,您可以使用其他支持的缓存实现,我只是更熟悉和习惯番石榴。如果可以将isExpired:V=>Boolean
更改为timeToLive:V=>Duration
,那么您可以使用
def refresh(k: String): Future[V] = readV(k) andThen {
case Success(v) => Cache.set(k, Future.successful(v), timeToLive(v))
}
def get(k: String): Future[V] = Cache.getOrElse(k)(refresh(k))
为了控制并发性,我喜欢actor模型:
object Lookup {
case class Get(key: String)
class LookupActor extends Actor {
def receive = {
case Get(key) =>
Cache.get(key) match {
case Some(v) => sender ! v
case None => val v = Await.result(readV(k), timeout)
Cache.set(key, v, timeToLive(v))
sender ! v
}
}
}
}
使用actor,如果我们有一个同步提供结果的readV
,那就太好了,因为actor模型提供了并发性(和控制)
客户端,它是:
val futureV = lookupActor ? Lookup.Get(key) mapTo[V]
谢谢,但我不是在寻找另一个缓存实现。您可以尝试实现我所描述的功能,并且如何防止并发刷新
?这就是我四年来一直关注的scala.concurrent.SyncVar
,或者使用scala.concurrent.blocking
和synchronized
的组合。或者您可以使用一个actor,因为一次只有一个线程在actor实例中运行。根据我的过期情况,在actor进程中进行块调用可能会导致各种问题,保证单一访问的唯一方法是使用单个块路径。文档解释说,当你需要做的时候,阻塞是可以的,这很少发生,但听起来你需要它。一个更好的方法是提供一个同步的readV
函数,这样你就不需要阻止当前线程等待将来。请注意,使用Await
是为了一个非常旧(现在)的Akka版本。今天,我们不用等待,而是使用Akka或中的管道模式进行理解。