我正在使用 LoadingCache:
val cacheloader =
new CacheLoader[Key, Value]() {
override def load(key: Key): Value = loadKeyFunc(key, None)
override def reload(key: Key, prevValue: Value): ListenableFuture[Value] = {
val task = ListenableFutureTask.create(new Callable[Value]() {
def call(): Value = {
loadKeyFunc(key, Some(prevValue))
}
})
executor.execute(task)
return task
}
}
val cache: LoadingCache[FirstPageSearch, Array[String]] =
CacheBuilder.newBuilder()
.maximumSize(10)
.refreshAfterWrite(5, TimeUnit.MINUTES)
.build(cacheLoader)
loadKeyFunc
只是一个 val loadKeyFunc: (Key, Option[Value]) => Value
形式的匿名函数。
cacheLoader 使用执行器 (Executors.newFixedThreadPool(6)
) 来使刷新异步。
系统接收到的 HTTP 请求始终会经过此缓存(向缓存发出 get(key)
),并始终从缓存中获取结果。当它太旧时,它会在后台重新计算并在下一个请求时提供它。
几天甚至几周内一切正常。但有时(通常是在非常低的使用时间内)缓存会停止刷新。新请求开始接收始终相同的旧数据。我在 loadKeyFunc
中有一个日志语句,我知道它没有被调用。
由于某种原因,LoadingCache
似乎没有看到数据早于 5 分钟。
当我重新启动系统(HTTP服务器)后,一切又恢复正常了。
有什么想法吗?
PS:我们使用的 loadKeyFunc 只是一个简单的日志语句,然后调用一个无状态对象,该对象查询我们的搜索后端系统,返回一个字符串数组(每个数组位置都是一个搜索页面)。
PS2:它是一个基于 Scalatra 的运行嵌入式 Jetty HTTP 服务器。 LoadingCache
在 ScalatraServlet
对象内创建。
一个小的清理日志(/first_page是始终使用缓存的请求,“Executing...”是CacheLoader
的(re)load方法内的日志语句) :
[INFO] [qtp48202314-8659] 2012-11-03 04:55:58 - Request(/first_page)
[INFO] [pool-2-thread-10] 2012-11-03 04:55:58 - Executing FirstPageSearch to put in cache
[INFO] [qtp48202314-8659] 2012-11-03 05:19:17 - Request(/first_page)
[INFO] [qtp48202314-8659] 2012-11-03 05:20:32 - Request(/first_page)
[INFO] [qtp48202314-8661] 2012-11-03 05:25:22 - Request(/first_page)
[INFO] [qtp48202314-8659] 2012-11-03 05:26:09 - Request(/first_page)
[INFO] [qtp48202314-8659] 2012-11-03 05:26:18 - Request(/first_page)
[INFO] [qtp48202314-8661] 2012-11-03 05:38:37 - Request(/first_page)
[INFO] [qtp48202314-8659] 2012-11-03 06:54:36 - Request(/first_page)
[INFO] [qtp48202314-26] 2012-11-03 11:31:37 - Request(/first_page)
[INFO] [pool-2-thread-1] 2012-11-03 11:31:37 - Executing FirstPageSearch to put in cache
[INFO] [qtp48202314-25] 2012-11-03 11:41:53 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 14:48:58 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 14:54:45 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:31:32 - Request(/first_page)
[INFO] [qtp48202314-26] 2012-11-03 15:31:48 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:32:05 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:44:44 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:44:44 - Request(/first_page)
[INFO] [qtp48202314-26] 2012-11-03 15:47:39 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:51:20 - Request(/first_page)
[INFO] [qtp48202314-26] 2012-11-03 15:52:59 - Request(/first_page)
[INFO] [qtp48202314-8674] 2012-11-03 15:54:18 - Request(/first_page)
[INFO] [qtp48202314-26] 2012-11-03 15:55:37 - Request(/first_page)
最佳答案
来自 CacheBuilder.refreshAfterWrite
Javadoc:
Currently automatic refreshes are performed when the first stale request for an entry occurs. The request triggering refresh will make a blocking call to
CacheLoader.reload(K, V)
and immediately return the new value if the returned future is complete, and the old value otherwise.
因此,当某个值过时时,直到您实际查询该键时才会触发刷新,此时它将返回旧值并异步触发刷新。一旦该值完成刷新,它将开始从缓存返回。
您确定这不是您看到此行为的原因吗?
想到的另一种可能性是,由于某种原因,您的 loadKeyFunc 没有终止。在固定大小的线程池中,如果您有六个查询因任何原因而锁定,则可能会完全阻止新查询进入线程池,这似乎会导致您观察到的问题。也许您应该使用 Executors.newCachedThreadPool,这可以避免该问题 - 尽管您仍然会遇到由锁定线程引起的内存泄漏。 =/
关于java - LoadingCache停止自动刷新并仅提供陈旧数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13212153/