我们在几个EC2实例上运行了一个Scala Play Framework应用程序,它在redis中缓存了很多case类:如果对象在缓存中,则返回缓存的对象,否则从数据库中检索对象,放入缓存,并返回。但是,有一些对象经常被引用(例如,许多对象包含一个或多个Location
s,并且所有Location
中约有20%只是整个美国的对象) ,如果我们在Redis
缓存之上引入弱哈希映射,我们可以减少内存消耗。
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
检索记录,然后将它们转换为适当的类型。应用程序首先在LocalCache
中查找对象;如果它存在,则返回该对象,否则应用程序在Redis
中查找该对象;如果它存在,则返回该对象,否则应用程序从数据库中检索该对象。如果Redis
或LocalCache
中缺少该对象,则会添加该对象; LocalCache
使用temp.key
将对象添加到弱哈希映射中,以便它保存对哈希键的字符串引用(但是如果对象意外地获得了GC,那么这就是子最佳,但不会破坏任何东西)。
问题是我们没有办法让LocalCache
失效 - 目前如果美国名称发生变化,我们会从{{1}清除Location
(我们不担心已经在内存中的Redis
个对象,例如,如果Location
User
是美国,我们将Location
名称更改为美国,然后Location
仍会看到他们在美国获得新的浏览器会话,但我们需要一种方法来传达缓存失效到所有EC2实例,以便他们可以从User
中删除Location
。我们可以做一些事情,例如在LocalCache
中为对象添加版本号或时间戳,如果对象的版本号/时间戳与对象的版本号/时间戳不匹配,则Redis
会使对象无效在Redis中,但这意味着我们将进行两次LocalCache
次来电而不是一次。
理想情况下,我们希望在每个EC2实例上设置一个邮箱,我们可以向其传递缓存失效消息。我们已经有一个RabbitMQ实例用于向ElasticSearch实例发送消息,但是我不确定EC2中是否已经有这样的实用工具 - 我是EC2的完整新手。
在多个EC2实例中散布弱哈希映射中的密钥失效的最佳方法是什么?
答案 0 :(得分:0)
由于您已经在使用RabbitMQ,我建议您使用它来最小化堆栈中的技术数量。
亚马逊确实拥有消息服务,简单队列服务(SQS),但您还需要使用其他亚马逊服务简单通知服务(SNS)来获取发布/订阅功能。请参阅常见问题解答"问:如何将相同的消息扇出到多个SQS队列?"在http://aws.amazon.com/sqs/faqs/。