繁体   English   中英

使用MapMaker#makeComputingMap防止同一数据同时进行RPC

[英]Using MapMaker#makeComputingMap to prevent simultaneous RPCs for the same data

我们有一个速度较慢的后端服务器,该服务器受到负载的挤压,我们希望中间层Scala服务器对于每个唯一的查询仅对后端有一个未完成的请求。

后端服务器仅存储不可变的数据,但是在添加新数据后,中间层服务器将代表客户端请求最新数据,并且后端服务器难以承受负载。 使用写入时生成的唯一键将不可变数据缓存在memcached中,但是写入速率很高,因此我们得到的memcached命中率较低。

我有一个想法是使用Google Guava的MapMaker#makeComputingMap()包装实际的查找,并在ConcurrentMap#get()返回之后,中间层将保存结果,只是从Map中删除键。

尽管代码很容易编写,但这似乎有点浪费,请参见下面的示例说明。

是否有更自然的数据结构,库或番石榴的一部分可以解决此问题?

import com.google.common.collect.MapMaker

object Test
{
  val computer: com.google.common.base.Function[Int,Long] =
  {
    new com.google.common.base.Function[Int,Long] {
      override
      def apply(i: Int): Long =
      {
        val l = System.currentTimeMillis + i
        System.err.println("For " + i + " returning " + l)
        Thread.sleep(2000)
        l
      }
    }
  }

  val map =
  {
    new MapMaker().makeComputingMap[Int,Long](computer)
  }

  def get(k: Int): Long =
  {
    val l = map.get(k)
    map.remove(k)
    l
  }

  def main(args: Array[String]): Unit =
  {
    val t1 = new Thread() {
      override def run(): Unit =
      {
        System.err.println(get(123))
      }
    }

    val t2 = new Thread() {
      override def run(): Unit =
      {
        System.err.println(get(123))
      }
    }

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    System.err.println(get(123))
  }
}

我不确定为什么要实施删除自己的工作,为什么不简单地具有弱或软值并让GC为您清理呢?

new MapMaker().weakValues().makeComputingMap[Int, Long](computer)

我认为您的做法很合理。 您仅使用该结构来获取密钥上的锁条,以确保对同一密钥的访问冲突。 不用担心您不需要每个键的值映射。 ConcurrentHashMap和friends是Java库+ Guava中唯一为您提供锁条的结构。

这确实会导致一些次要的运行时开销,以及您不需要的哈希表的大小(如果对同一段的访问堆积并且remove()无法跟上的话,哈希表的大小甚至可能会增加)。

如果您想使其尽可能便宜,则可以自己编写一些简单的带锁代码。 基本上是N个锁(N =并发级别)的Object[] (或Array[AnyRef] :)),您只需将查找键的哈希映射到该数组中并进行锁定。 这样做的另一个优点是,您实际上不必执行CHM所需的哈希码技巧,因为后者必须将哈希码分为一个部分来选择锁,而另一部分则用于哈希表的需要,但是您可以将其全部用于锁定选择。

编辑 :在下面草绘我的评论:

val concurrencyLevel = 16
val locks = (for (i <- 0 to concurrencyLevel) yield new AnyRef).toArray

def access(key: K): V = {
   val lock = locks(key.hashCode % locks.size)
   lock synchronized {
     val valueFromCache = cache.lookup(key)
     valueFromCache match {
       case Some(v) => return v
       case None =>
         val valueFromBackend = backendServer.lookup(key)
         cache.put(key, valueFromBackend)
         return valueFromBackend
     }
   }
}

(顺便说一句,是否需要toArray调用?或者返回的IndexSeq已经可以通过索引快速访问了?)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM