ScalaのPlayFrameworkでのキャッシュ
1. 概要
このチュートリアルでは、PlayのキャッシングAPIをScalaで使用する方法を学習します。 このAPIは、驚くほどシンプルですが、低速のシステムから結果を再フェッチするのではなく、結果をキャッシュしたい多くの状況に対応します。
2. キャッシングの有効化
PlayアプリケーションでキャッシングAPIを使用するには、プロジェクトレベルで実行するいくつかの設定があります。
まず、プロジェクトのbuild.sbtを追加の依存関係で修正する必要があります。
libraryDependencies += caffeine
PlayはEhcacheの使用もサポートしていますが、この記事の執筆時点ではCaffeineを推奨しています。
その依存関係をプロジェクトに追加したら、 sbt を再起動するか、IDEでプロジェクトをリロードする必要があります。 これが完了すると、コードでキャッシングAPIの使用を開始する準備が整います。
3. Play SyncCacheApiおよびAsyncCacheApi
Playは、同期と非同期の2種類のキャッシュを提供します。 ここでは、 Webサービスにより適しているため、非同期バージョンに焦点を当てます。
AsyncCacheApi は、いくつかの操作をサポートしています。
- set(key:String、value:Any、expiry:Duration = duration.Inf):Future [Done] –指定された時間後に期限切れになる値をキャッシュに設定します(デフォルトは決してありません)
- remove(key:String):Future [Done] –指定された値をキャッシュから削除します
- get [T](key:String):Future [Option [T]] –キャッシュが存在する場合はキャッシュから値を取得します
- getOrElseUpdate [T](key:String、expiry:Duration)(orElse:=> Future [T]):Future [T] –存在する場合は指定された値を取得し、存在しない場合は
orElse[を呼び出しますX166X]そのキーの値を設定する関数 - removeAll():Future [Done] –キャッシュからすべてのエントリを削除します
APIの同期バージョンであるSyncCacheApiは、 removeAll()メソッドを提供しないことに注意してください。
次に、 getOrElseUpdate()メソッドの使用方法を説明します。
4. キャッシングAPIの使用
例として、Twitterの最近の検索APIを使用する単純なアプリケーションにキャッシングAPIを使用します。 このAPIは、指定されたユーザーの過去7日間のツイートを返します。
ここでキャッシュを使用することには、いくつかの理由があります。
- Twitterは、APIを呼び出す頻度を制限しています
- ほとんどのTwitterユーザーはそれほど頻繁にツイートしません
サンプルコードでは、Twitterユーザー名をキャッシュへのキーとして使用します。 また、エントリは5分後に期限切れになるように設定します。 これは、application.confファイルのtwitterCache.expiry設定を介して構成できます。
4.1. Webコントローラー
トップレベルのクラスは、TwitterControllerと呼ばれるPlayBaseControllerのインスタンスです。
@Singleton
class TwitterController @Inject()(
twitterSearchService: TwitterSearchService,
override val controllerComponents: ControllerComponents,
implicit val executionContext: ExecutionContext
) extends BaseController {
def recentSearch(twitterAccount: String): Action[AnyContent] = Action.async {
twitterSearchService.recentSearch(twitterAccount).map { response =>
Ok(Json.toJson(response))
}
}
}
Play routers ファイルでコントローラーを構成することにより、外部の呼び出し元がコントローラーを使用できるようにします。
GET /api/twitter/recentSearch/:twitterAccount controllers.TwitterController.recentSearch(twitterAccount)
Playアプリケーションを起動すると、curlを使用して上記のインターフェイスを操作できます。
curl http://localhost:9000/api/twitter/recentSearch/TwitterDev | jq
次に、キャッシュを使用する場所であるサービスレイヤーをどのように実装したかを見てみましょう。
4.2. 検索サービス
サービスレイヤーコードも非常にシンプルで、ここでPlayが提供するキャッシングAPIを使用します。
class TwitterSearchService @Inject()(twitterWebApi: TwitterWebApi,
cache: AsyncCacheApi,
configuration: Configuration,
implicit val executionContext: ExecutionContext) {
val cacheExpiry: Duration = configuration.get[Duration]("twitterCache.expiry")
def recentSearch(twitterUser: String): Future[Map[String, JsValue]] = {
cache.getOrElseUpdate[JsValue](twitterUser, cacheExpiry) {
twitterWebApi.recentSearch(twitterUser)
}.map(_.as[Map[String, JsValue]])
}
}
上で説明したように、 getOrElseUpdate()メソッドは、指定されたツイートが存在する場合はキャッシュから取得します。存在しない場合は、TwitterAPIのラッパーを呼び出して最新の値を取得します。 これは、どのキャッシュでも非常に一般的な使用パターンです。 大多数のアプリケーションでは、この1つのAPI呼び出しでおそらく十分です。
4.3. APIクライアント
ツイートをフェッチするクライアントコードは、Play WSClient クラスを使用し、独自のキャッシュを行いません。
def recentSearch(fromTwitterUser: String): Future[JsValue] = {
val url = String.format(recentSearchUrl, fromTwitterUser)
wsClient
.url(url)
.withHttpHeaders(
HeaderNames.ACCEPT -> MimeTypes.JSON,
HeaderNames.AUTHORIZATION -> s"Bearer $bearerToken"
).get()
.map { response =>
if (response.status == OK) {
response.json
} else {
throw ApiError(response.status, Some(response.statusText))
}
}
}
Twitter APIでは、認証にベアラートークンを使用する必要があることに注意してください。 これは、Twitter開発者アカウントを作成すると発行されます。 また、Play WSRequest.withAuth()メソッドは、基本認証などのユーザー名とパスワードを持つ認証メソッドに対してのみ機能するため、ヘッダーを直接設定する必要があることに注意してください。
4.4. PlayCachingAPIに関する最終メモ
SyncCacheApiもAsyncCacheApiもスレッド間同期を提供しないことに注意してください。 つまり、特定のキーに対して、使用したAPIによってTwitterが複数回呼び出される可能性があります。
さらに、ここで使用したカフェインプロバイダーは分散キャッシュではありません。 したがって、Playアプリの複数のインスタンスを実行すると、それぞれに独自のキャッシュがあります。 これを提供するようにEhcacheを構成することが可能です。
5. 結論
このチュートリアルでは、PlayのキャッシングAPIの使用について紹介および説明しました。 ご覧のとおり、Playを使用すると、この機能をアプリケーションに簡単に追加できます。
いつものように、完全なソースコードはGitHubでから入手できます。