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日間のツイートを返します。

ここでキャッシュを使用することには、いくつかの理由があります。

  1. Twitterは、APIを呼び出す頻度を制限しています
  2. ほとんどの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に関する最終メモ

SyncCacheApiAsyncCacheApiもスレッド間同期を提供しないことに注意してください。 つまり、特定のキーに対して、使用したAPIによってTwitterが複数回呼び出される可能性があります。

さらに、ここで使用したカフェインプロバイダーは分散キャッシュではありません。 したがって、Playアプリの複数のインスタンスを実行すると、それぞれに独自のキャッシュがあります。 これを提供するようにEhcacheを構成することが可能です。

5. 結論

このチュートリアルでは、PlayのキャッシングAPIの使用について紹介および説明しました。 ご覧のとおり、Playを使用すると、この機能をアプリケーションに簡単に追加できます。

いつものように、完全なソースコードはGitHubから入手できます。