我们有一个运行在几个 EC2 实例上的 Scala Play Framework 应用程序,它在 redis 中缓存了很多案例类:如果对象在缓存中,则返回缓存的对象,否则从数据库中检索对象,放入缓存,并返回。但是,有一些对象经常被引用(例如,许多对象包含一个或多个 Location
,并且大约 20% 的 Location
只是用于美国作为一个整体),因此如果我们在 Redis
缓存之上引入一个弱 HashMap ,我们就可以减少内存消耗。
object GetKey {
def getKey(id: Int, clazz: Class[_]) = clazz.getSimpleName + ":" + id
}
abstract class Record(implicit manifest: Manifest[Record]) {
val id: Int
final val key = GetKey.getKey(id, manifest.getClass)
}
object LocalCache {
private val map = new mutable.WeakHashMap[String, Record]
def fetch[T](key: String, value: => T) = {
map.get(key) match {
case Some(t) => t.asInstanceOf[T]
case _ => {
val temp = value
if(temp.key != key) throw new IllegalArgumentException("key mismatch")
map.put(temp.key, temp)
temp
}}}}
object Redis {
def fetch[T](key: String, value: => T) = {
this.get(key) match {
case Some(t) => t.asInstanceOf[T]
case _ => {
val temp = value
this.put(key, value)
temp
}}}}
object RecordDAO {
val recordClass: Class[_] // same as Record's manifest.getClass
def getById(id: Int): Record = {
def getFromDb(id): Record = { ... }
def getFromRedis(key: String): Record = Redis.fetch(key, getFromDb(id))
def getFromLocalCache(key: String): Record = LocalCache.fetch(key, getFromRedis(key))
getFromLocalCache(GetKey.getKey(id, recordClass))
}
}
Redis
代码已经到位,LocalCache
代码是我们要添加的代码。所有记录类型都继承自 Record
,因此 Redis
和 LocalCache
检索 Records,然后将它们转换为适当的类型。应用首先在LocalCache
中寻找一个对象;如果存在,则返回对象,否则应用程序在 Redis
中查找该对象;如果存在则返回对象,否则应用程序从数据库中检索对象。如果 Redis
和/或 LocalCache
中缺少该对象,则会添加该对象; LocalCache
使用 temp.key
将对象添加到弱 HashMap 中,这样它将保存对哈希键的字符串引用(但是,如果对象不小心被 GC那么早,这是次优的,但不会破坏任何东西。
问题是我们没有使 LocalCache
无效的好方法 - 目前如果例如美国的名称发生变化,那么我们会清除 Location
来自 Redis
(我们不必担心已经在内存中的 Location
对象,例如,如果 User
的 Location
是美国,我们将 Location
名称更改为 USA,那么 User
仍然会看到他们在美国,直到他们得到一个新的浏览器 session ),但我们需要一种方法将缓存失效传达给所有 EC2 实例,以便它们可以从它们的 LocalCache
中删除 Location
。我们可以做一些事情,比如向 Redis
中的对象添加版本号或时间戳,如果对象的版本号/时间戳不匹配,则让 LocalCache
使对象无效在 Redis 中,但这意味着我们将执行两个 Redis
get 调用而不是一个。
理想情况下,我们希望在每个 EC2 实例上设置一个邮箱,我们可以将缓存失效消息传递到该邮箱。我们已经有一个用于将消息发送到 ElasticSearch 实例的 RabbitMQ 实例,但我不确定 EC2 中是否已经有用于此类事情的实用程序 - 我是 EC2 的完全新手。
让分布在多个 EC2 实例中的弱 HashMap 中的 key 失效的最佳方法是什么?
最佳答案
由于您已经在使用 RabbitMQ,我建议您使用它来尽量减少堆栈中的技术数量。
Amazon 确实有一个消息服务,简单队列服务 (SQS),但您还需要使用另一个 Amazon 服务,Simple Notification Service (SNS),以获得发布/订阅功能。请参阅常见问题解答“问:如何将相同的消息扇出到多个 SQS 队列?”在 http://aws.amazon.com/sqs/faqs/ .
关于scala - 使跨多个服务器的弱 HashMap 无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22394915/