【问题标题】:How to implement immutable cache in Scala?如何在 Scala 中实现不可变缓存?
【发布时间】:2015-06-07 21:51:11
【问题描述】:

假设我有一个服务器,它根据客户端请求调用一个耗时的函数slow: Int => String。如果slow 未在timeout 内返回,则服务器向客户端返回错误。

def trySlow(timeout: Duration)(id: Int): Try[String] = {
  val fut = Future(slow(id))
  try {
    Await.ready(fut, timeout).value match {
      case Some(r) => r
      case None => Failure(new TimeoutException()) // should not happen
    }
  } catch {
     case e: TimeoutException => Failure(e)
  } 
}

现在我想缓存未来,以便使用相同id 调用trySlow 的多个线程将等待相同的未来。

我将使用一个可变并发TrieMap来实现一个单例缓存。

case class CacheEntry (
  future: Future[String], 
  start: Long = System.currentTimeMillis() // need it to clean the cache
) 

type Cache = TrieMap[Int, CacheEntry]

def trySlow(timeout: Duration, cache: Cache)(id: Int): Try[String] = {

  val fut = cache.getOrElseUpdate(id,  CacheEntry(Future(slow(id))))

  ... // as in above
}

这有意义吗? immutable non-singleton 缓存如何做到这一点?

【问题讨论】:

    标签: scala caching concurrency future


    【解决方案1】:

    如果你只想使用 scala 集合中的东西,scala.collection.concurrent.TrieMap 是一个不错的选择。但是,请注意 TrieMap#getOrElseUpdate 有一个 thread safety bug,最近才在 2.11.6 中修复。

    如果你能负担得起额外的依赖,guava cache 非常适合编写这样的缓存。特别是如果您希望缓存中的条目以某种方式过期。

    关于缓存的 API:假设您在谈论纯函数,缓存生成器应该只是一个接受函数 T => U 并返回函数 T => U

    所以大致如下:

    object Cached {
      def apply[T,U](f: T=>U): T=>U = { ??? }
    }
    

    用法:

    def slow(id: Int): Try[String] = ??? // something complex including futures, timeouts etc.
    val fast: Int => Try[String] = Cached(slow)
    

    缓存 API 不需要知道关于被缓存的函数的任何信息,除了你期望它是纯的。

    【讨论】:

    • 感谢您提及该错误。我现在在 2.11.6 上运行,所以不会有问题。
    【解决方案2】:

    我建议您一般使用番石榴库。 (https://code.google.com/p/guava-libraries/)

    就像 Rüdiger Klaehn 提到的,缓存是一个很好的起点。

    【讨论】:

      猜你喜欢
      • 2017-01-14
      • 2014-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 1970-01-01
      • 2018-08-17
      相关资源
      最近更新 更多