§Play キャッシュ API
データのキャッシュは、最新のアプリケーションにおける一般的な最適化であり、Play はグローバルキャッシュを提供します。
**注:** キャッシュに関する重要な点は、それがキャッシュとして期待されるように動作することです。つまり、保存したばかりのデータが消失する可能性があります。
キャッシュに保存されたデータについては、データが消失した場合に備えて、再生成戦略を策定する必要があります。この考え方は Play の基本原則の1つであり、セッションがそのライフタイムを通じて値を保持することが期待される Java EE とは異なります。
Play は、Caffeine をベースとした `CacheApi` 実装と、Ehcache 2.x をベースとしたレガシー実装を提供します。プロセス内キャッシングには、通常 Caffeine が最適な選択肢です。分散キャッシングが必要な場合は、memcached と redis のサードパーティープラグインがあります。
§Cache API のインポート
Play は、Cache API と Caffeine および Ehcache 実装のための個別の依存関係を提供します。
§Caffeine
Caffeine 実装を取得するには、依存関係リストに `caffeine` を追加します。
libraryDependencies ++= Seq(
caffeine
)
これにより、コンポーネントを注入可能にするためのランタイム DI のバインディングも自動的に設定されます。
§EhCache
EhCache 実装を取得するには、依存関係リストに `ehcache` を追加します。
libraryDependencies ++= Seq(
ehcache
)
これにより、コンポーネントを注入可能にするためのランタイム DI のバインディングも自動的に設定されます。
§カスタムキャッシュ実装
API のみを追加するには、依存関係リストに `cacheApi` を追加します。
libraryDependencies ++= Seq(
cacheApi
)
API 依存関係は、特定のキャッシュ実装に依存することなく、Cached
ヘルパーや AsyncCacheApi
などの独自のバインディングを定義する場合に便利です。カスタムキャッシュモジュールを作成する場合は、これを使用する必要があります。
§Cache API へのアクセス
キャッシュ API は、非同期実装と同期実装のどちらが必要かによって、AsyncCacheApi と SyncCacheApi トレイトによって定義され、他の依存関係と同様にコンポーネントに注入できます。例えば
import javax.inject.Inject
import play.api.cache._
import play.api.mvc._
class Application @Inject() (cache: AsyncCacheApi, cc: ControllerComponents) extends AbstractController(cc) {}
**注:** この API は、複数の実装をプラグインできるように意図的に最小限に抑えられています。より具体的な API が必要な場合は、キャッシュプラグインによって提供される API を使用してください。
このシンプルな API を使用して、キャッシュにデータを保存できます。
val result: Future[Done] = cache.set("item.key", connectedUser)
そして後で取得します。
val futureMaybeUser: Future[Option[User]] = cache.get[User]("item.key")
キャッシュから取得するか、値が存在しない場合はキャッシュに値を設定する便利なヘルパーもあります。
val futureUser: Future[User] = cache.getOrElseUpdate[User]("item.key") {
User.findById(connectedUser)
}
**注:** `getOrElseUpdate` は EhCache ではアトミック操作ではなく、`get` に続いて値を計算し、次に `set` として実装されています。これは、複数のスレッドが同時に `getOrElse` を呼び出している場合、値が複数回計算される可能性があることを意味します。
期間を渡すことで有効期限を指定できます。デフォルトでは期間は無期限です。
import scala.concurrent.duration._
val result: Future[Done] = cache.set("item.key", connectedUser, 5.minutes)
キャッシュからアイテムを削除するには、`remove` メソッドを使用します。
val removeResult: Future[Done] = cache.remove("item.key")
キャッシュからすべてのアイテムを削除するには、`removeAll` メソッドを使用します。
val removeAllResult: Future[Done] = cache.removeAll()
`removeAll()` は `AsyncCacheApi` でのみ使用可能です。これは、キャッシュのすべての要素を削除することは、同期的に実行したいこととしてはまれであるためです。キャッシュからすべてのアイテムを削除することは、特別な場合の管理操作としてのみ必要であり、アプリケーションの通常の操作の一部ではないと予想されます。
SyncCacheApi は、Future を使用せずに値を直接返すことを除いて、同じ API を持っています。
§異なるキャッシュへのアクセス
名前で異なる設定を持つ異なるキャッシュを定義して使用することができます。異なるキャッシュにアクセスするには、注入する際に、依存関係に NamedCache 修飾子を使用します。例えば
import javax.inject.Inject
import play.api.cache._
import play.api.mvc._
class Application @Inject() (
@NamedCache("session-cache") sessionCache: AsyncCacheApi,
cc: ControllerComponents
) extends AbstractController(cc) {}
複数の異なるキャッシュにアクセスする場合は、次のように `application.conf` でバインドするように Play に指示する必要があります。
play.cache.bindCaches = ["db-cache", "user-cache", "session-cache"]
名前付きキャッシュの定義と設定は、使用しているキャッシュの実装によって異なります。Caffeine を使用した名前付きキャッシュの設定例を以下に示します。
§Caffeine を使用した名前付きキャッシュの設定
すべてのキャッシュのフォールバックとして使用されるデフォルトのカスタム設定を渡すには、次のように指定できます。
play.cache.caffeine.defaults = {
initial-capacity = 200
...
}
特定のキャッシュのカスタム設定データを渡すには、次のようにします。
play.cache.caffeine.user-cache = {
initial-capacity = 200
...
}
§EhCache を使用した名前付きキャッシュの設定
EhCache 実装では、デフォルトのキャッシュは play と呼ばれ、ehcache.xml というファイルを作成することで設定できます。追加のキャッシュは、異なる設定、または実装で設定できます。
デフォルトでは、Play は `play.cache.bindCaches` から名前のキャッシュを作成しようとします。`ehcache.xml` で自分で定義する場合は、次のように設定できます。
play.cache.createBoundCaches = false
§実行コンテキストの設定
デフォルトでは、Caffeine と EhCache はメモリに要素を格納します。そのため、ブロッキング I/O はほとんどないため、キャッシュへの読み書きは非常に高速です。
ただし、キャッシュの設定方法によっては(たとえば、EhCache の `DiskStore` を使用する場合)、ブロッキング I/O が発生し、コストが高くなる可能性があります。これは、非同期実装でもデフォルトの実行コンテキストでスレッドがブロックされるためです。
このような場合は、異なる Pekko ディスパッチャ を構成し、`play.cache.dispatcher` を介して設定することで、キャッシュプラグインがそれを利用できるようにします。
play.cache.dispatcher = "contexts.blockingCacheDispatcher"
contexts {
blockingCacheDispatcher {
fork-join-executor {
parallelism-factor = 3.0
}
}
}
§Caffeine
Caffeine を使用する場合、これは Caffeine の 内部実行プログラム を設定します。実際、`play.cache.dispatcher` を設定すると、`play.cache.caffeine.defaults.executor` が設定されます。上記で説明したように、異なるキャッシュに異なる実行プログラムを設定できます。
play.cache.caffeine.user-cache = {
executor = "contexts.anotherBlockingCacheDispatcher"
...
}
§EhCache
EhCache の場合、Play は指定されたディスパッチャのスレッドで Future 上で EhCache 操作を実行します。
§HTTP 応答のキャッシュ
標準的な Action の合成を使用して、スマートなキャッシュされたアクションを簡単に作成できます。
**注:** Play HTTP の `Result` インスタンスは、後で安全にキャッシュして再利用できます。
Cached クラスは、キャッシュされたアクションを構築するのに役立ちます。
import javax.inject.Inject
import play.api.cache.Cached
class Application @Inject() (cached: Cached, cc: ControllerComponents) extends AbstractController(cc) {}
`"homePage"` のような固定キーを使用して、アクションの結果をキャッシュできます。
def index = cached("homePage") {
Action {
Ok("Hello world")
}
}
結果が異なる場合は、それぞれ異なるキーを使用して結果をキャッシュできます。この例では、各ユーザーに異なるキャッシュされた結果があります。
def userProfile = WithAuthentication(_.session.get("username")) { userId =>
cached(req => "profile." + userId) {
Action.async {
User.find(userId).map { user => Ok(views.html.profile(user)) }
}
}
}
§キャッシュの制御
キャッシュする内容やキャッシュから除外する内容を簡単に制御できます。
200 OK の結果のみをキャッシュする場合があります。
def get(index: Int) = cached.status(_ => "/resource/" + index, 200) {
Action {
if (index > 0) {
Ok(Json.obj("id" -> index))
} else {
NotFound
}
}
}
または、404 Not Found を数分間だけキャッシュします。
def get(index: Int) = {
val caching = cached
.status(_ => "/resource/" + index, 200)
.includeStatus(404, 600)
caching {
Action {
if (index % 2 == 1) {
Ok(Json.obj("id" -> index))
} else {
NotFound
}
}
}
}
§カスタム実装
キャッシュ API のカスタム実装を提供できます。`cacheApi` 依存関係があることを確認してください。
次に、AsyncCacheApi を実装し、DIコンテナにバインドします。SyncCacheApi を DefaultSyncCacheApi にバインドすることもできます。これは非同期実装を単純にラップしたものです。
キャッシュ実装によっては、不可能であるか、または不必要に非効率であるため、removeAll
メソッドがサポートされない場合があります。その場合は、removeAll
メソッドでUnsupportedOperationException
をスローできます。
デフォルトの実装に加えてキャッシュAPIの実装を提供するには、カスタム修飾子を作成するか、NamedCache
修飾子を再利用して実装をバインドします。
§カスタム実装と併用したCaffeineの使用
Caffeineのデフォルト実装を使用するには、caffeine
依存関係が必要であり、application.conf
でCaffeineモジュールの自動バインドを無効にする必要があります。
play.modules.disabled += "play.api.cache.caffeine.CaffeineCacheModule"
§カスタム実装と併用したEhCacheの使用
EhCacheのデフォルト実装を使用するには、ehcache
依存関係が必要であり、application.conf
でEhCacheモジュールの自動バインドを無効にする必要があります。
play.modules.disabled += "play.api.cache.ehcache.EhCacheModule"
このドキュメントに誤りを見つけましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインをお読みになった後、プルリクエストを自由に送ってください。ご質問やアドバイスがありましたら、コミュニティフォーラムでコミュニティとの会話を開始してください。