0

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 は executor ( Executors.newFixedThreadPool(6)) を使用して更新を非同期にします。システムは、常にこのキャッシュを通過する (キャッシュに aget(key)を発行する) HTTP 要求を受信し、常にキャッシュから結果をフェッチします。古すぎる場合は、バックグラウンドで再計算し、次のリクエストで提供します。

数日、場合によっては数週間、すべてが正常に機能します。しかし、ときどき (通常、使用量が非常に少ない時間帯に) キャッシュの更新が停止します。新しいリクエストは常に同じ古いデータを受け取り始めます。内部にログ ステートメントがloadKeyFuncあり、それが呼び出されていないことがわかっています。

何らかの理由LoadingCacheで、データが 5 分よりもかなり古いことが表示されていないようです。システム (HTTP サーバー) を再起動すると、すべて正常に戻ります。

何か案は?

PS: 使用する loadKeyFunc は単純なログ ステートメントの後に、検索バックエンド システムにクエリを実行して String 配列を返すステートレス オブジェクトへの呼び出しが続きます (各配列位置は検索ページです)。

PS2: 組み込みの Jetty HTTP サーバーを実行する Scalatra ベースです。はオブジェクトLoadingCache内に作成されScalatraServletます。

クリーンアップされた小さなログ (/first_page は常にキャッシュを使用する要求であり、"Executing..." は の (再) ロード メソッド内のログ ステートメントですCacheLoader):

[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)
4

2 に答える 2

1

CacheBuilder.refreshAfterWriteJavadocから:

現在、エントリに対する最初の古いリクエストが発生すると、自動更新が実行されます。リフレッシュをトリガーするリクエストは、ブロッキング呼び出しをCacheLoader.reload(K, V)行い、返されたフューチャが完了している場合はすぐに新しい値を返し、それ以外の場合は古い値を返します。

そのため、値が古い場合、そのキーを実際にクエリするまで更新はトリガーされません。その時点で、古い値が返され、更新が非同期的にトリガーされます。値の更新が完了するとすぐに、キャッシュから値が返され始めます。

それがこの動作を見ている理由ではありませんか?

頭に浮かぶ他の可能性は、何らかの理由で loadKeyFunc が終了していないことです。固定サイズのスレッドプールでは、何らかの理由でロックされたクエリが 6 つある場合、新しいクエリがスレッドプールに入るのをまったくブロックしている可能性があり、観察した問題を正確に引き起こすようです。おそらくExecutors.newCachedThreadPool、その問題を回避する を使用する必要がありますが、ロックアップされたスレッドが原因でメモリ リークが発生することはあります。=/

于 2012-11-03T18:30:55.673 に答える
0

を使用すると、古いデータの提供を回避できますexpireAfterWrite。これにより、古いデータを提供しないことが確実に保証されます。通常は と組み合わせることexpireAfterWriteをお勧めrefreshAFterWriteしますが、更新が実行されるまでの時間を確保するために、有効期限が切れるまでの遅延を大きくすることをお勧めします。

于 2012-11-05T10:16:35.480 に答える