Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala play cache——根据未来的价值来记忆过期的未来_Scala_Caching_Asynchronous_Playframework 2.0 - Fatal编程技术网

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或
中的管道模式进行理解。